/*
 * @Copyright: (C) 2019 Zhang Rida. All rights reserved.
 *
 * @Author: Zhang Rida
 * @Email: 215154317@qq.com
 * @FilePath: /WEILA_SDK_MODULE/src/wlsdk/src/burst_module.c
 * @Date: 2021-07-15 18:41:53
 * @Description:
 * 会话业务:
 * 个人会话优先与群会话
 * 个人会话有保活时间,保活时间没通讯是否个人会话
 * 群会话只播放attach群,非attach群消息过滤
 * 个人会话保活期,群消息挂期
 * TTS与回放语音优先级最高
 * @LastEditors: Zhang Rida
 * @LastEditTime: 2021-07-16 19:53:42
 */
#include "burst_module.h"

#include <stdio.h>

#include "burst_media.h"
#include "group_module.h"
#include "keepalive_module.h"
#include "protocol_client.h"
#include "repo.h"
#include "session_module.h"
#include "tts_client.h"
#include "weilapb.h"
#include "weilapb_defines.h"
#include "wlsdk_define.h"

#ifdef __cplusplus
extern "C" {
#endif

#define BURST_VOIP_LIFE_LEFT 45000
#define PLAYBACK_IDX_RESET_INTERVAL 5                                     // 回放索引5秒重置
#define BURST_SWITCH_IDX_RESET_INTERVAL 10                                // 切群索引10秒重置
#define BURST_CHECK_TIMER_INTERVAl 1000                                   // 1000ms检查不可修改,影响界面刷新
#define BURST_SECOND_FRAMES (1000 / BURST_FRAME_TIME)                     // 每秒帧数
#define BURST_PTT_MSG_WAIT_TIME (10 * 1000)                               // AUDIO消息播放结束超时10秒
#define BURST_AUDIO_MSG_WAIT_TIME 1000                                    // AUDIO消息播放结束超时1s
#define BURST_MSG_LIFETIME_MAX ((WL_MEMORY_PTT_CACHE_MAX / 2048) * 1000)  // 消息最长生命期 PTT缓存最大时长
#define BURST_MSG_LIFETIME_TEXT (65 * 1000)                               // 文本合成最长生命期 30s
#define BURST_MSG_LIFETIME_TTS (30 * 1000)                                // TTS语音最长生命期 30s
#define BURST_MSG_LIFETIME_PLAYBACK (65 * 1000)                           // playback语音最长生命期 65s

#define BURST_DELAY_TIMER_INTERVAl 100  // 延时100ms释放
#define BURST_GROUP_TMP_NAME "临时呼叫"
#define BURST_SERVICE_NAME "服务呼叫"

typedef struct __burst_request {
    wlsdk_session session;
    wl_uint16     seq;
    wl_uint32     totalcount;
    wl_int32      resultcode;
} burst_request;

typedef struct __burst_voip {
    wl_char            callid[MAX_URL_LEN];
    wl_int32           seq;
    wl_int32           lifeleft;  // 生命期 默认30秒
    wl_uint32          framecount;
    wlsdk_session_voip session_voip;
} burst_voip;

typedef struct {
    wl_bool                initialized;
    wlsdk_message_callback callback;
    wl_uint32              msg_seq;
    wl_list                msg_list;
    wl_list                monitor_list;
    wl_bool                isburst;        // 是否在对讲
    wl_bool                burst_pressed;  // PTT按下状态
    burst_request          burst_request;
    burst_voip             burst_voip;
    wl_uint32              burst_seq;                  // 讲话Seq
    wl_uint16              playback_idx;               // 回放序号
    wl_time                playback_finish_timestamp;  // 回放结束时间
    wl_uint16              burst_switch_idx;           // 切会话序号
    wl_time                burst_switch_timestamp;     // 切会话时间
    WL_TIMER               timer;
    WL_TIMER               burst_delay_timer;  // PTT验收释放定时器
    WL_TIMER               voip_delay_timer;   // voip延时自动接听定时器
    wl_bool                voip_auto_accept;
    wl_uint32              voip_delay_ms;
    wl_bool                force_pause;     // 语音强制暂停
    wl_int32               force_lifeleft;  // 语音强制暂停剩余时间

    wl_uint64 active_sessionid;    // 活跃会话Id(与repo不一致时通知UI变更)
    wl_uint16 active_sessiontype;  // 活跃会话类型
    wl_int32  active_lifeleft;     // 活跃会话保留剩余时间
} wl_burst_global;

typedef enum {
    BURST_MSGTYPE_TEXT     = 0,
    BURST_MSGTYPE_AUDIO    = 1,
    BURST_MSGTYPE_PTT      = 2,
    BURST_MSGTYPE_TTS      = 3,
    BURST_MSGTYPE_REALTIME = 4,
    BURST_MSGTYPE_PLAYBACK = 5,
    BURST_MSGTYPE_VOIP     = 6,
} burst_msgtype;

typedef struct __burst_monitor {
    wl_uint32 monitorid;
    wl_int32  lifetime;
    wl_uint32 seq;
    wl_uint32 packageSeq;
    wl_uint32 marker;
} burst_monitor;

typedef struct __burst_msgsession {
    wlsdk_session session;
    wl_uint32     senderid;
    wl_char       sendername[MAX_NAME_LEN];
    wl_uint32     msgid;
    wl_uint64     created;
} burst_msgsession;

typedef struct __burst_pttframe {
    wl_uint16 framecount;
    wl_uint16 frameslen;
    wl_char   frames[ZERO_CONTENT_SIZE];
} burst_pttpackage;

typedef struct __burst_pttmsg {
    wl_uint16 marker;
    wl_uint16 seq;
    wl_uint16 packageseq;
    wl_uint16 source;
    wl_uint16 playcount;  // 播放帧数
    wl_uint16 framecount;
    wl_uint32 frameslen;
    wl_list   package_list;
} burst_pttmsg;

typedef struct __burst_audiomsg {
    wl_uint16 playcount;  // 播放帧数
    wl_uint16 framecount;
    wl_uint32 audiolen;
    wl_char   audio[ZERO_CONTENT_SIZE];
} burst_audiomsg;

typedef struct __burst_textmsg {
    wl_bool   issynthesis;  // 是否合成
    wl_uint16 textlen;
    wl_char   text[ZERO_CONTENT_SIZE];
} burst_textmsg;

typedef struct __burst_msg {
    wl_uint32        seq;
    burst_msgsession msgsession;
    wl_uint32        msgtype;     // TEXT PTT AUDIO PLAYBACK
    wl_int32         lifetime;    // 消息生命期
    wl_uint32        alloccount;  // 分配内存大小
    wl_int32         prority;     // 消息优先级
    wl_uint32        contentlen;  // 内容大小
    wl_char content[ZERO_CONTENT_SIZE];  // 依据不同的类型有对应不同的数据类型 TTS wl_char PTT burst_pttmsg TEXT
                                         //  burst_textmsg AUDIO
                                         //  PLAYBACK burst_audiomsg
} burst_msg;

typedef struct __burst_record_data {
    wl_uint32 marker;
    wl_uint64 sessionid;
    wl_uint32 sessiontype;
    void*     context;
    wl_uint32 framecount;
    wl_uint32 frameslen;
    wl_char   frames[ZERO_CONTENT_SIZE];
} burst_record_data;

typedef struct __burst_play_result {
    wl_uint32 framecount;
    void*     context;
} burst_play_result;

typedef struct __burst_end_result {
    wl_int32 resultcode;
    wl_bool  isclosed;
    void*    context;
} burst_end_result;

static wl_burst_global s_burst_global;

static wl_int32
    burst_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static wl_int32 burst_protocol_client_listen_callback(PBWeilaMsg* message, void* context);
static wl_int32 burst_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen);
static void     burst_tts_client_callback(wl_uint32 result,
                                          wl_char*  text,
                                          tts_type  type,
                                          wl_char*  content,
                                          wl_uint32 content_len,
                                          void*     context);
static void
    burst_process_tts_audio(wl_char* text, tts_type type, wl_char* content, wl_uint32 contentlen, void* context);
static void    burst_listen_callback(void);
static void    burst_check(void);
static void    burst_burst_meida_recorder_callback(wl_uint32 marker,
                                                   wl_uint32 frameslen,
                                                   wl_char*  frames,
                                                   wl_uint32 framecount,
                                                   wl_uint64 sessionid,
                                                   wl_uint32 sessiontype,
                                                   void*     context);
static void    burst_burst_meida_recorder_end_callback(wl_int32 result, wl_bool isclose, void* context);
static void    burst_burst_media_player_callback(wl_uint32 playcount, void* context);
static void    burst_burst_media_player_end_callback(wl_int32 result, wl_bool isclose, void* context);
static void    burst_monitor_start(wl_uint32 monitorid, wl_uint32 duration);
static void    burst_monitor_stop(wl_uint32 monitorid);
static void    burst_release_session(wl_uint64 sessionid, wl_uint32 sessiontype);
static void    burst_ptt_release(void);
static void    burst_stop_player(void);
static void    burst_stop_player_immediately(void);
static void    burst_stop_recorder(wl_int32 result);
static void    burst_message_free(burst_msg* msg);
static void    burst_idle(wlsdk_session* session, wl_int32 resultcode);
static wl_bool burst_is_session_message_allow_play(burst_msg* msg);
static void    process_burst_request(void* data, wl_int32 datalen);
static void    process_burst_release(void* data, wl_int32 datalen);
static void    process_burst_playback(void* data, wl_int32 datalen);
static void    process_burst_tts(void* data, wl_int32 datalen);
static void    process_burst_set_burst_session(void* data, wl_int32 datalen);
static void    process_burst_remove_session(void* data, wl_int32 datalen);
static void    process_burst_close_session(void* data, wl_int32 datalen);
static void    process_burst_realtime_audio(void* data, wl_int32 datalen);
static void    process_burst_record_data(void* data, wl_int32 datalen);
static void    process_monitor_record_data(void* data, wl_int32 datalen);
static void    process_burst_record_end(void* data, wl_int32 datalen);
static void    process_burst_play_result(void* data, wl_int32 datalen);
static void    process_burst_play_end(void* data, wl_int32 datalen);
static void    process_burst_switch(void);

static void process_burst_msgdata_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void process_burst_control_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);

static void process_burst_msgdata_notify_message(PBWeilaMsg* message, void* context);
static void process_burst_control_notify_message(PBWeilaMsg* message, void* context);
static void process_monitor_control_notify_message(PBWeilaMsg* message, void* context);
static void process_text_notify_message(burst_msgsession* msgsession, void* content, wl_uint32 contentlen);
static void process_ptt_notify_message(burst_msgsession* msgsession, void* content, wl_uint32 contentlen);

static void burst_monitor_send_monitor_frames(burst_monitor* monitor, PBLinkList* framelist);

static void burst_burst_meida_voip_recorder_callback(wl_uint32 marker,
                                                     wl_uint32 frameslen,
                                                     wl_char*  frames,
                                                     wl_uint32 framecount,
                                                     wl_uint64 sessionid,
                                                     wl_uint32 sessiontype,
                                                     void*     context);
static void burst_burst_meida_voip_recorder_end_callback(wl_int32 result, wl_bool isclose, void* context);
static void burst_burst_media_voip_player_callback(wl_uint32 playcount, void* context);
static void burst_burst_media_voip_player_end_callback(wl_int32 result, wl_bool isclose, void* context);
static void process_burst_voip_record_data(void* data, wl_int32 datalen);
static void process_burst_voip_record_end(void* data, wl_int32 datalen);
static void process_burst_voip_play_result(void* data, wl_int32 datalen);
static void process_burst_voip_play_end(void* data, wl_int32 datalen);
static void process_burst_voip_invite(void* data, wl_int32 datalen);
static void process_burst_voip_accept(void* data, wl_int32 datalen);
static void process_burst_voip_hangup(void* data, wl_int32 datalen);
static void process_burst_set_voip_auto_accept(void* data, wl_int32 datalen);
static void process_burst_button_press(void* data, wl_int32 datalen);
static void process_burst_button_release(void* data, wl_int32 datalen);
static void process_burst_voip_invite_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void process_burst_voip_accept_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void process_voip_invite_notify_message(PBWeilaMsg* message, void* context);
static void process_voip_accept_notify_message(PBWeilaMsg* message, void* context);
static void process_voip_hangup_notify_message(PBWeilaMsg* message, void* context);
static void process_voip_notify_message(wl_uint32 userId, void* content, wl_uint32 contentlen);
static void burst_voip_hangup(void);
static void burst_voip_state(void);
static void burst_voip_ring(void);
static void burst_voip_incoming(void);
static void burst_voip_establish(void);
static void burst_voip_terminate(wl_int32 resultCode);
static wl_int32 burst_protocol_client_voip_request_callback(PBWeilaMsg* message,
                                                            void*       content,
                                                            wl_uint32   contentlen,
                                                            void*       context);

static void burst_pblink_add_tail(PBLinkList* list, wl_char* data, wl_uint32 datalen) {
    PBLinkItem* item = (PBLinkItem*)wl_malloc(sizeof(PBLinkItem) + datalen);
    if (item) {
        PBLinkItem* temp = wl_null;
        memset(item, 0, sizeof(PBLinkItem) + datalen);
        item->dataSize = datalen;
        memcpy(item->data, data, datalen);
        temp = list->pLinks;
        if (temp) {
            while (temp->pNext) {
                temp = temp->pNext;
            }
            temp->pNext = item;
        } else {
            list->pLinks = item;
        }
        list->listCount++;
    }
}

static void burst_pblink_free(PBLinkList* list) {
    PBLinkItem* item = list->pLinks;
    PBLinkItem* temp = list->pLinks;
    while (item) {
        temp = item;
        item = temp->pNext;
        wl_free(temp);
    }
}

static void burst_sdk_message_callback(wl_uint32 msgid, wl_int32 resultcode, void* msgdata, wl_uint32 datalen) {
    if (s_burst_global.callback) {
        WLLOGD("msgid %d", msgid);
        s_burst_global.callback(msgid, resultcode, msgdata, datalen);
    }
}

static void burst_set_active_session(repo_session* session) {
    if (0 != session->session.sessionid
        && (WLSDK_SESSION_TYPE_SINGLE == session->session.sessiontype
            || WLSDK_SESSION_TYPE_GROUP == session->session.sessiontype
            || WLSDK_SESSION_TYPE_SERVICE == session->session.sessiontype)) {
        repo_set_active_session(session);
        WLLOGD("session %lld type %d name %s active session %llu type %d",
               session->session.sessionid,
               session->session.sessiontype,
               session->session.name,
               s_burst_global.active_sessionid,
               s_burst_global.active_sessiontype);
        if (session->session.sessionid != s_burst_global.active_sessionid
            || session->session.sessiontype != s_burst_global.active_sessiontype) {
            s_burst_global.active_sessionid   = session->session.sessionid;
            s_burst_global.active_sessiontype = session->session.sessiontype;
            session->session.isdefault =
                repo_is_session_default(s_burst_global.active_sessionid, s_burst_global.active_sessiontype);
            burst_sdk_message_callback(
                WLSDK_MSG_SESSION_BURST_SESSION, WLSDK_RESULT_SUCCESS, &session->session, sizeof(wlsdk_session));
            // 防止会话切换旧会话话权不更新
            burst_idle(&session->session, WLSDK_RESULT_SUCCESS);
            WLLOGD("burst session %lld type %d", session->session.sessionid, session->session.sessiontype);
            // 正式群会话要重新附着
            if (WLSDK_SESSION_TYPE_GROUP == session->session.sessiontype) {
                wl_core_send_message(WL_MODULE_SESSION, SESSION_MSG_ATTACH_SESSION, wl_null, 0);
            }
        }
    } else {
        WLLOGD("unknown session type");
    }
}

static wl_int32 burst_check_active_session(repo_session* session) {
    if (0 == repo_get_active_session(session)) {
        if (session->session.sessionid != s_burst_global.active_sessionid
            || session->session.sessiontype != s_burst_global.active_sessiontype) {
            WLLOGI("session %lld type %d", session->session.sessionid, session->session.sessiontype);
            s_burst_global.active_sessionid   = session->session.sessionid;
            s_burst_global.active_sessiontype = session->session.sessiontype;
            burst_sdk_message_callback(
                WLSDK_MSG_SESSION_BURST_SESSION, WLSDK_RESULT_SUCCESS, &session->session, sizeof(wlsdk_session));
            // 防止会话切换旧会话话权不更新
            burst_idle(&session->session, WLSDK_RESULT_SUCCESS);
            WLLOGD("burst session %lld type %d", session->session.sessionid, session->session.sessiontype);
        }
        return 0;
    }
    return -1;
}

static void burst_delete_session_message(wl_uint64 sessionid, wl_uint16 sessiontype) {
    wl_list_iterator iterator;
    burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // 删除会话消息
        if (msg->msgsession.session.sessionid == sessionid && msg->msgsession.session.sessiontype == sessiontype) {
            // 如果删除会话有消息在播放
            if (msg->seq == (wl_uint32)burst_media_get_player_context()) {
                burst_media_stop_player();
                burst_idle(&msg->msgsession.session, WLSDK_RESULT_SUCCESS);
            }
            WLLOGI("wl_list_iterator_remove seq %d", msg->seq);
            burst_message_free(msg);
            msg = (burst_msg*)wl_list_iterator_remove(&iterator);
            continue;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
}

static void burst_clean_message_by_msgtype(wl_int32 msgtype) {
    wl_list_iterator iterator;
    burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // 删除会话消息
        if (msgtype == msg->msgtype) {
            WLLOGI("wl_list_iterator_remove seq %d", msg->seq);
            burst_message_free(msg);
            msg = (burst_msg*)wl_list_iterator_remove(&iterator);
            continue;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
}

// 统计msg_list缓存内存占用
static wl_uint32 burst_cached_count(void) {
    // msg_list 占用最大内存数统计
    wl_list_iterator iterator;
    wl_uint32        count = 0;
    burst_msg*       msg   = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        count += msg->alloccount;
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
    return count;
}

static burst_msg* burst_get_message(wl_uint32 seq) {
    wl_list_iterator iterator;
    burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (msg->seq == seq) {
            return msg;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
    return wl_null;
}

// 获取优先播放消息
// 1、消息类型为realtime、tts、playback,只比较消息优先级
// 2、消息类型为text、audio、ptt,比较会话优先级,只有会话优先级大于活跃会话或当前活跃会话消息才允许播放
static burst_msg* burst_get_prority_message(void) {
    wl_list_iterator iterator;
    burst_msg*       proritymsg = wl_null;
    burst_msg*       msg        = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);

    while (!wl_list_iterator_is_end(&iterator)) {
        wl_int32 msgsession_prority =
            repo_get_session_prority(msg->msgsession.session.sessionid, msg->msgsession.session.sessiontype);
        if (wl_null == proritymsg) {
            proritymsg = msg;
        }
        // Realtime消息TTS与回放消息按消息按优先级处理(这几类消息不比较会话优先级)
        if (BURST_MSGTYPE_REALTIME == msg->msgtype || BURST_MSGTYPE_TTS == msg->msgtype
            || BURST_MSGTYPE_PLAYBACK == msg->msgtype || BURST_MSGTYPE_REALTIME == proritymsg->msgtype
            || BURST_MSGTYPE_TTS == proritymsg->msgtype || BURST_MSGTYPE_PLAYBACK == proritymsg->msgtype) {
            if (msg->prority > proritymsg->prority) {
                proritymsg = msg;
            }
        }
        // 会话类比较消息
        else {
            repo_session active_session;
            wl_int32     proritymsgsession_prority = repo_get_session_prority(proritymsg->msgsession.session.sessionid,
                                                                          proritymsg->msgsession.session.sessiontype);
            memset(&active_session, 0, sizeof(repo_session));
            // 存在活跃会话且未超时,需要考虑活跃会话对优先级影响
            if (0 == burst_check_active_session(&active_session)) {
                // 不是活跃会话消息
                if ((msg->msgsession.session.sessionid != active_session.session.sessionid
                     || msg->msgsession.session.sessiontype != active_session.session.sessiontype)) {
                    wl_int32 activesession_prority =
                        repo_get_session_prority(active_session.session.sessionid, active_session.session.sessiontype);
                    // 消息会话优先级小于等于活跃会话优先级,消息不满足播放条件
                    if (msgsession_prority <= activesession_prority) {
                        if (proritymsg == msg) {
                            proritymsg = wl_null;
                        }
                        msg = (burst_msg*)wl_list_get_next(&iterator);
                        continue;
                    }
                }
            }
            // PTT消息要比较playcount与framecount
            if (BURST_MSGTYPE_PTT == msg->msgtype) {
                burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                // PTT消息已播放完(PTT消息可能未接收完整)
                if (pttmsg->playcount >= pttmsg->framecount) {
                    if (proritymsg == msg) {
                        proritymsg = wl_null;
                    }
                    msg = (burst_msg*)wl_list_get_next(&iterator);
                    continue;
                }
            }
            // audio消息要比较playcount与framecount
            if (BURST_MSGTYPE_AUDIO == msg->msgtype) {
                burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
                // Audio消息已播放完
                if (audiomsg->playcount >= audiomsg->framecount) {
                    if (proritymsg == msg) {
                        proritymsg = wl_null;
                    }
                    msg = (burst_msg*)wl_list_get_next(&iterator);
                    continue;
                }
            }
            // 文本消息如果同步TTS语音不处理
            if (BURST_MSGTYPE_TEXT == msg->msgtype) {
                burst_textmsg* textmsg = (burst_textmsg*)msg->content;
                if (textmsg->issynthesis) {
                    if (proritymsg == msg) {
                        proritymsg = wl_null;
                    }
                    msg = (burst_msg*)wl_list_get_next(&iterator);
                    continue;
                }
            }
            // 比较会话优先级
            if (msgsession_prority > proritymsgsession_prority) {
                proritymsg = msg;
            }
            // 相同会话优先级
            else if (msgsession_prority == proritymsgsession_prority) {
                // 比较消息优先级
                if (msg->prority > proritymsg->prority) {
                    proritymsg = msg;
                } else if (msg->prority == proritymsg->prority) {
                    // 合成状态文本消息低于其他类型消息
                    if (BURST_MSGTYPE_TEXT != msg->prority && BURST_MSGTYPE_TEXT == proritymsg->msgtype) {
                        burst_textmsg* textmsg = (burst_textmsg*)proritymsg->content;
                        if (textmsg->issynthesis) {
                            proritymsg = msg;
                        }
                    } else {
                        // 生命期短优先播放
                        if (msg->lifetime <= proritymsg->lifetime) {
                            proritymsg = msg;
                        }
                    }
                }
            }
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
    return proritymsg;
}

static void burst_message_free(burst_msg* msg) {
    if (BURST_MSGTYPE_PTT == msg->msgtype) {
        burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
        wl_list_free(&pttmsg->package_list);
    }
    wl_free(msg);
}

static void burst_delete_message(wl_uint32 seq) {
    wl_list_iterator iterator;
    burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (msg->seq == seq) {
            WLLOGI("wl_list_iterator_remove seq %d", msg->seq);
            burst_message_free(msg);
            wl_list_iterator_remove(&iterator);
            break;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
}

static void burst_idle(wlsdk_session* session, wl_int32 resultcode) {
    wlsdk_session_burst session_burst;
    repo_session        sessioninfo;
    memset(&session_burst, 0, sizeof(wlsdk_session_burst));
    s_burst_global.isburst = wl_false;
    if (0 == repo_get_session(session->sessionid, session->sessiontype, &sessioninfo)) {
        memcpy(&session_burst.session, &sessioninfo.session, sizeof(wlsdk_session));
        // 修改timeout方便UI显示倒计时
        if (!session_burst.session.isdefault) {
            session_burst.session.timeout =
                repo_get_session_burst_time_remaining(session->sessionid, session->sessiontype);
        } else {
            session_burst.session.timeout = 0;
        }
        session_burst.burststate = WLSDK_SESSION_BURST_IDLE;
        WLLOGD("session id %lld type %d resultcode %d", session->sessionid, session->sessiontype, resultcode);
        burst_sdk_message_callback(
            WLSDK_MSG_SESSION_BURST_STATE, resultcode, &session_burst, sizeof(wlsdk_session_burst));
    }
}

static void burst_listen(wlsdk_session* session,
                         wl_uint32      playcount,
                         wl_uint32      framecount,
                         wl_uint32      talkerid,
                         wl_char*       talkername) {
    wlsdk_session_burst session_burst;
    repo_session        sessioninfo;
    memset(&session_burst, 0, sizeof(wlsdk_session_burst));
    s_burst_global.isburst = wl_true;
    if (0 == repo_get_session(session->sessionid, session->sessiontype, &sessioninfo)) {
        memcpy(&session_burst.session, &sessioninfo.session, sizeof(wlsdk_session));
        // 修改timeout方便UI显示倒计时
        if (!session_burst.session.isdefault) {
            session_burst.session.timeout =
                repo_get_session_burst_time_remaining(session->sessionid, session->sessiontype);
        } else {
            session_burst.session.timeout = 0;
        }
        playcount                = playcount > framecount ? framecount : playcount;
        session_burst.burststate = WLSDK_SESSION_BURST_LISTEN;
        session_burst.playtime   = playcount / 50;
        session_burst.duration   = (framecount + 49) / 50;
        session_burst.talkerid   = talkerid;
        strncpy(session_burst.talkername, talkername, sizeof(session_burst.talkername));
        session_burst.talkername[sizeof(session_burst.talkername) - 1] = '\0';
        WLLOGD("session id %lld type %d talkerid %d playtime %d duration %d",
               session->sessionid,
               session->sessiontype,
               talkerid,
               session_burst.playtime,
               session_burst.duration);
        burst_sdk_message_callback(
            WLSDK_MSG_SESSION_BURST_STATE, WLSDK_RESULT_SUCCESS, &session_burst, sizeof(wlsdk_session_burst));
    }
}

static void burst_talk(wlsdk_session* session, wl_uint32 framecount) {
    wlsdk_session_burst session_burst;
    repo_session        sessioninfo;
    memset(&session_burst, 0, sizeof(wlsdk_session_burst));
    s_burst_global.isburst = wl_true;
    if (0 == repo_get_session(session->sessionid, session->sessiontype, &sessioninfo)) {
        repo_user userinfo;
        repo_get_selfinfo(&userinfo);
        memcpy(&session_burst.session, &sessioninfo.session, sizeof(wlsdk_session));
        // 修改timeout方便UI显示倒计时
        if (!session_burst.session.isdefault) {
            session_burst.session.timeout =
                repo_get_session_burst_time_remaining(session->sessionid, session->sessiontype);
        } else {
            session_burst.session.timeout = 0;
        }
        session_burst.talkerid = userinfo.user.userid;
        if (strlen(userinfo.user.nick)) {
            strncpy(session_burst.talkername, userinfo.user.nick, sizeof(session_burst.talkername) - 1);
        } else {
            strncpy(session_burst.talkername, userinfo.user.number, sizeof(session_burst.talkername) - 1);
        }
        session_burst.burststate = WLSDK_SESSION_BURST_TALK;
        session_burst.duration   = (framecount + 49) / 50;
        session_burst.playtime   = session_burst.duration;
        burst_sdk_message_callback(
            WLSDK_MSG_SESSION_BURST_STATE, WLSDK_RESULT_SUCCESS, &session_burst, sizeof(wlsdk_session_burst));
    }
}

wl_int32 burst_module_init(wlsdk_message_callback callback) {
    wl_int32  ret = 0;
    wl_module module;

    WLLOGI("burst_module_init");
    if (!s_burst_global.initialized) {
        if (0 != burst_media_init()) {
            WLLOGW("burst_meida_init fail");
            return -1;
        }

        memset(&module, 0, sizeof(wl_module));
        module.moduleid = WL_MODULE_BURST;
        module.taskid   = WL_TASK_APP;
        module.callback = burst_message_callback;
        ret             = wl_core_register_module(&module);
        if (0 != ret) {
            WLLOGW("wl_core_register_module burst fail");
            burst_media_end();
            return ret;
        }

        s_burst_global.callback         = callback;
        s_burst_global.voip_auto_accept = wl_true;
        s_burst_global.voip_delay_ms    = 3000;

        protocol_client_add_listen(
            WL_SESSION_MSGDATA_MSG, WEILA_MSG_IND, burst_protocol_client_listen_callback, wl_null);
        protocol_client_add_listen(
            WL_SESSION_BURST_CTRL_MSG, WEILA_MSG_IND, burst_protocol_client_listen_callback, wl_null);
        protocol_client_add_listen(
            WL_SESSION_MONITOR_CTRL_MSG, WEILA_MSG_IND, burst_protocol_client_listen_callback, wl_null);

        protocol_client_add_listen(
            WL_SESSION_VOIP_INVITE_MSG, WEILA_MSG_IND, burst_protocol_client_listen_callback, wl_null);
        protocol_client_add_listen(
            WL_SESSION_VOIP_ACCEPT_MSG, WEILA_MSG_IND, burst_protocol_client_listen_callback, wl_null);
        protocol_client_add_listen(
            WL_SESSION_VOIP_HANGUP_MSG, WEILA_MSG_IND, burst_protocol_client_listen_callback, wl_null);

        s_burst_global.initialized = wl_true;
    }

    return ret;
}

wl_int32 burst_module_end(void) {
    WLLOGI("burst_module_end");
    if (s_burst_global.initialized) {
        wl_list_iterator iterator;
        burst_msg*       msg = wl_null;
        burst_media_end();
        wl_core_unregister_module(WL_MODULE_BURST);
        s_burst_global.callback = wl_null;

        msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
        // 检查超时消息
        while (!wl_list_iterator_is_end(&iterator)) {
            if (BURST_MSGTYPE_PTT == msg->msgtype) {
                burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                wl_list_free(&pttmsg->package_list);
            }
            msg = (burst_msg*)wl_list_get_next(&iterator);
        }
        wl_list_free(&s_burst_global.msg_list);
        if (WL_INVALID_TIMER != s_burst_global.timer) {
            wl_timer_stop(s_burst_global.timer);
            s_burst_global.timer = WL_INVALID_TIMER;
        }

        s_burst_global.initialized = wl_false;
    }

    return 0;
}

static void burst_timer_callback(WL_TIMER timer, void* context) {
    wl_list_iterator iterator;
    burst_msg*       msg     = wl_null;
    burst_monitor*   monitor = wl_null;
    repo_session     active_session;
    if (timer != s_burst_global.timer) {
        WLLOGW("timer %d is not equal local timer %d", timer, s_burst_global.timer);
        wl_timer_stop(timer);
        return;
    }
    // voip优先处理
    if (BURST_MEDIA_PRORITY_VOIP == burst_media_get_player_prority()
        || BURST_MEDIA_PRORITY_VOIP == burst_media_get_recorder_prority()) {
        // voip超时检查
        if (s_burst_global.burst_voip.session_voip.state != WLSDK_VOIP_NONE) {
            s_burst_global.burst_voip.lifeleft -= BURST_CHECK_TIMER_INTERVAl;
            // 挂机
            if (s_burst_global.burst_voip.lifeleft <= 0) {
                burst_voip_hangup();
            }
        } else {
            burst_media_stop_voip();
        }
    }
    if (s_burst_global.force_pause) {
        s_burst_global.force_lifeleft -= BURST_CHECK_TIMER_INTERVAl;
    }
    if (s_burst_global.active_lifeleft > 0) {
        s_burst_global.active_lifeleft -= BURST_CHECK_TIMER_INTERVAl;
    }

    msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        msg->lifetime -= BURST_CHECK_TIMER_INTERVAl;
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }

    monitor = (burst_monitor*)wl_list_get_first(&s_burst_global.monitor_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        monitor->lifetime -= BURST_CHECK_TIMER_INTERVAl;
        monitor = (burst_monitor*)wl_list_get_next(&iterator);
    }

    if (s_burst_global.burst_pressed && burst_media_is_recording()) {
        burst_talk(&s_burst_global.burst_request.session, s_burst_global.burst_request.totalcount);
        repo_refresh_session_burst(s_burst_global.burst_request.session.sessionid,
                                   s_burst_global.burst_request.session.sessiontype);
    }

    if (burst_media_is_playing()) {
        burst_listen_callback();
    }

    WLLOGI("burst_check");
    burst_check();

    memset(&active_session, 0, sizeof(active_session));
    // 活跃会话
    if (0 == burst_check_active_session(&active_session)) {
        // 话权空闲
        if (!s_burst_global.burst_pressed && !burst_media_is_playing()) {
            burst_idle(&active_session.session, WLSDK_RESULT_SUCCESS);
        }
        // 话权不释放
        if (!repo_is_session_burst_release(active_session.session.sessionid, active_session.session.sessiontype)) {
            return;
        }
    }
    // 没有播放消息且PTT键松开且没有监听,停止定时器,没有强制打断,没有voip
    if (0 >= wl_list_size(&s_burst_global.msg_list) && !s_burst_global.burst_pressed
        && 0 >= wl_list_size(&s_burst_global.monitor_list) && 0 >= s_burst_global.active_lifeleft
        && (!s_burst_global.force_pause || s_burst_global.force_lifeleft < 0)) {
        if (WL_INVALID_TIMER != s_burst_global.timer) {
            wl_timer_stop(s_burst_global.timer);
            s_burst_global.timer = WL_INVALID_TIMER;
        }
    }
}

static void burst_tts_client_callback(wl_uint32 result,
                                      wl_char*  text,
                                      tts_type  type,
                                      wl_char*  content,
                                      wl_uint32 contentlen,
                                      void*     context) {
    WLLOGD("text len %d result %d content len %d", text ? strlen(text) : 0, result, contentlen);
    if (WLSDK_RESULT_SUCCESS == result) {
        burst_process_tts_audio(text, type, content, contentlen, context);
        burst_check();
    } else {
        // 文字消息转语音
        if (TTS_PTT == type) {
            if (context) {
                burst_msg* msg = burst_get_message((wl_uint32)context);
                if (msg) {
                    WLLOGD("text msg seq %d tts error", msg->seq);
                    burst_delete_message(msg->seq);
                }
            }
        }
    }
}

static void
    burst_process_tts_audio(wl_char* text, tts_type type, wl_char* content, wl_uint32 contentlen, void* context) {
    // 解析数据
    // 头4~10字节为#!opus
    if (0 == strncasecmp("#!opus", (wl_char*)(content + 4), 6) && contentlen > 10) {
        wl_uint32  frameslen  = 0;
        wl_uint32  framecount = 0;
        burst_msg* newmsg     = wl_null;
        burst_media_count_burst_audio((wl_char*)(content + 10), contentlen - 10, &frameslen, &framecount);
        WLLOGI("opus frameslen %d framecount %d", frameslen, framecount);

        newmsg = (burst_msg*)wl_malloc(sizeof(burst_msg) + sizeof(burst_audiomsg) + frameslen);
        if (newmsg) {
            burst_audiomsg* audiomsg = wl_null;
            memset(newmsg, 0, (sizeof(burst_msg) + sizeof(burst_audiomsg) + frameslen));
            newmsg->alloccount = sizeof(burst_msg) + sizeof(burst_audiomsg) + frameslen;
            newmsg->seq        = ++s_burst_global.msg_seq;
            // 文字消息转语音
            if (TTS_PTT == type) {
                if (context) {
                    burst_msg* msg = burst_get_message((wl_uint32)context);
                    if (msg) {
                        // 复制文字消息会话信息,删除文字消息.消息类型变更为AUDIO
                        memcpy(&newmsg->msgsession, &msg->msgsession, sizeof(burst_msgsession));
                        burst_delete_message(msg->seq);
                        newmsg->msgtype  = BURST_MSGTYPE_AUDIO;
                        newmsg->prority  = BURST_MEDIA_PRORITY_AUDIO;
                        newmsg->lifetime = BURST_MSG_LIFETIME_MAX;
                    } else {
                        burst_message_free(newmsg);
                        WLLOGW("text msg seq %d is not exist", (wl_uint32)context);
                        return;
                    }
                }
            } else if (TTS_TTS == type) {
                // 删除TTS消息(TTS消息唯一)
                burst_clean_message_by_msgtype(BURST_MSGTYPE_TTS);
                newmsg->msgtype  = BURST_MSGTYPE_TTS;
                newmsg->prority  = BURST_MEDIA_PRORITY_TTS;
                newmsg->lifetime = BURST_MSG_LIFETIME_TTS;
                if (text) {
                    repo_set_tts_audio(text, strlen(text), content, contentlen);
                }
            } else {
                burst_message_free(newmsg);
                WLLOGW("tts type is not support");
                return;
            }

            newmsg->contentlen   = sizeof(burst_audiomsg) + frameslen;
            audiomsg             = (burst_audiomsg*)newmsg->content;
            audiomsg->framecount = framecount;
            audiomsg->playcount  = 0;
            audiomsg->audiolen   = frameslen;
            WLLOGI("opus framecount %d frameslen %d", framecount, frameslen);
            burst_media_convert_burst_audio_to_media_audio(
                (wl_char*)(content + 10), contentlen - 10, audiomsg->audio, frameslen);
            if (-1 == wl_list_add(&s_burst_global.msg_list, newmsg, 0)) {
                burst_message_free(newmsg);
                WLLOGW("list add fail");
                return;
            }
        } else {
            WLLOGI("malloc fail");
        }
    }
}

static void burst_process_tts_pcm_compress(wl_uint32 rate, wl_char* content, wl_uint32 contentlen) {
    wl_uint32  media_audio_len = contentlen;
    wl_uint32  frames_count    = contentlen / OPUS_FRAME_LEN;
    burst_msg* newmsg          = wl_null;
    newmsg                     = (burst_msg*)wl_malloc(sizeof(burst_msg) + sizeof(burst_audiomsg) + media_audio_len);
    WLLOGD("pcm media_audio_len %d frames_count %d contentlen %d", media_audio_len, frames_count, contentlen);
    if (newmsg) {
        burst_audiomsg* audiomsg = wl_null;
        memset(newmsg, 0, (sizeof(burst_msg) + sizeof(burst_audiomsg) + media_audio_len));
        newmsg->alloccount = sizeof(burst_msg) + sizeof(burst_audiomsg) + media_audio_len;
        newmsg->seq        = ++s_burst_global.msg_seq;

        // 删除TTS消息(TTS消息唯一)
        burst_clean_message_by_msgtype(BURST_MSGTYPE_TTS);
        newmsg->msgtype      = BURST_MSGTYPE_TTS;
        newmsg->prority      = BURST_MEDIA_PRORITY_TTS;
        newmsg->lifetime     = BURST_MSG_LIFETIME_TTS;
        newmsg->contentlen   = sizeof(burst_audiomsg) + media_audio_len;
        audiomsg             = (burst_audiomsg*)newmsg->content;
        audiomsg->framecount = frames_count;
        audiomsg->playcount  = 0;
        audiomsg->audiolen   = media_audio_len;
        memcpy(audiomsg->audio, content, media_audio_len);
        if (-1 == wl_list_add(&s_burst_global.msg_list, newmsg, 0)) {
            burst_message_free(newmsg);
            WLLOGW("list add fail");
            return;
        }
    } else {
        WLLOGI("malloc fail");
    }
}

static void burst_process_tts_pcm(wl_uint32 rate, wl_char* content, wl_uint32 contentlen) {
    wl_uint32  media_audio_len = 0;
    wl_uint32  frames_count    = 0;
    burst_msg* newmsg          = wl_null;
    burst_media_count_pcm_audio(contentlen, rate, &media_audio_len, &frames_count);
    newmsg = (burst_msg*)wl_malloc(sizeof(burst_msg) + sizeof(burst_audiomsg) + media_audio_len);
    WLLOGD("pcm media_audio_len %d frames_count %d contentlen %d", media_audio_len, frames_count, contentlen);
    if (newmsg) {
        burst_audiomsg* audiomsg = wl_null;
        memset(newmsg, 0, (sizeof(burst_msg) + sizeof(burst_audiomsg) + media_audio_len));
        newmsg->alloccount = sizeof(burst_msg) + sizeof(burst_audiomsg) + media_audio_len;
        newmsg->seq        = ++s_burst_global.msg_seq;

        // 删除TTS消息(TTS消息唯一)
        burst_clean_message_by_msgtype(BURST_MSGTYPE_TTS);
        newmsg->msgtype      = BURST_MSGTYPE_TTS;
        newmsg->prority      = BURST_MEDIA_PRORITY_TTS;
        newmsg->lifetime     = BURST_MSG_LIFETIME_TTS;
        newmsg->contentlen   = sizeof(burst_audiomsg) + media_audio_len;
        audiomsg             = (burst_audiomsg*)newmsg->content;
        audiomsg->framecount = frames_count;
        audiomsg->playcount  = 0;
        audiomsg->audiolen   = media_audio_len;
        if (0
            == burst_media_convert_pcm_audio_to_media_audio(
                content, contentlen, rate, audiomsg->audio, &media_audio_len)) {
            if (-1 == wl_list_add(&s_burst_global.msg_list, newmsg, 0)) {
                burst_message_free(newmsg);
                WLLOGW("list add fail");
                return;
            }
        } else {
            burst_message_free(newmsg);
            WLLOGW("burst_media_convert_pcm_audio_to_media_audio fail");
            return;
        }
    } else {
        WLLOGI("malloc fail");
    }
}

static wl_int32 burst_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen) {
    WLLOGT("msgid %d", msgid);
    switch (msgid) {
        case BURST_MSG_REQUEST:
            // 用户离线
            if (WLSDK_LOGIN_SYNCING != repo_get_login_state() && WLSDK_LOGIN_ONLINE != repo_get_login_state()) {
                burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_REQUEST, WLSDK_RESULT_LOCAL_OFFLINE, wl_null, 0);
                burst_media_play_tone(BURST_MEDIA_TONE_OFFLINE,
                                      burst_burst_media_player_end_callback,
                                      BURST_MEDIA_PRORITY_ALARM,
                                      0,
                                      (void*)wl_null);
                return -1;
            }
            break;
        default:
            break;
    }
    switch (msgid) {
        case BURST_MSG_REQUEST:
            // 非voip才允许对讲
            if (BURST_MEDIA_PRORITY_VOIP == burst_media_get_recorder_prority()
                || BURST_MEDIA_PRORITY_VOIP == burst_media_get_player_prority()) {
                break;
            }
            if (!s_burst_global.force_pause || s_burst_global.force_lifeleft < 0) {
                process_burst_request(data, datalen);
            }
            break;
        case BURST_MSG_RELEASE:
            // 非voip才允许对讲
            if (BURST_MEDIA_PRORITY_VOIP == burst_media_get_recorder_prority()
                || BURST_MEDIA_PRORITY_VOIP == burst_media_get_player_prority()) {
                break;
            }
            if (!s_burst_global.force_pause || s_burst_global.force_lifeleft < 0) {
                process_burst_release(data, datalen);
            }
            break;
        case BURST_MSG_PLAYBACK:
            // 非voip才允许对讲
            if (BURST_MEDIA_PRORITY_VOIP == burst_media_get_recorder_prority()
                || BURST_MEDIA_PRORITY_VOIP == burst_media_get_player_prority()) {
                break;
            }
            process_burst_playback(data, datalen);
            break;
        case BURST_MSG_TTS:
            // 非voip才允许对讲
            if (BURST_MEDIA_PRORITY_VOIP == burst_media_get_recorder_prority()
                || BURST_MEDIA_PRORITY_VOIP == burst_media_get_player_prority()) {
                break;
            }
            process_burst_tts(data, datalen);
            break;
        case BURST_MSG_REALTIME_AUDIO:
            // 非voip才允许对讲
            if (BURST_MEDIA_PRORITY_VOIP == burst_media_get_recorder_prority()
                || BURST_MEDIA_PRORITY_VOIP == burst_media_get_player_prority()) {
                break;
            }
            process_burst_realtime_audio(data, datalen);
            break;
        case BURST_MSG_RECORD_DATA: {
            burst_record_data* record_data = (burst_record_data*)data;
            burst_monitor*     monitor     = wl_list_get(&s_burst_global.monitor_list, 0);

            // 监听语音
            if (monitor && record_data->sessionid == monitor->monitorid
                && record_data->sessiontype == WLSDK_SESSION_TYPE_SINGLE) {
                process_monitor_record_data(data, datalen);
            }
            // 对讲语音
            else if (0 != record_data->sessionid
                     && record_data->sessionid == s_burst_global.burst_request.session.sessionid
                     && record_data->sessiontype == s_burst_global.burst_request.session.sessiontype) {
                process_burst_record_data(data, datalen);
            }
            break;
        }

        case BURST_MSG_RECORD_END: {
            process_burst_record_end(data, datalen);
            break;
        }
        case BURST_MSG_PLAY_RESULT:
            process_burst_play_result(data, datalen);
            break;
        case BURST_MSG_PLAY_END:
            process_burst_play_end(data, datalen);
            break;
        case BURST_MSG_SET_BURST_SESSION:
            process_burst_set_burst_session(data, datalen);
            break;
        case BURST_MSG_REMOVE_SESSION:
            process_burst_remove_session(data, datalen);
            break;
        case BURST_MSG_CLOSE_SESSION:
            process_burst_close_session(data, datalen);
            break;
        case BURST_MSG_CHECK:
            break;
        case BURST_MSG_PAUSE: {
            wl_int32* duration = (wl_int32*)data;

            s_burst_global.force_pause    = wl_true;
            s_burst_global.force_lifeleft = (*duration) * 1000;
            burst_stop_recorder(WLSDK_RESULT_SUCCESS);
            burst_stop_player_immediately();
            burst_voip_hangup();
            break;
        }
        case BURST_MSG_RESUME:
            s_burst_global.force_pause = wl_false;
            break;
        case BURST_MSG_VOIP_INVITE:
            process_burst_voip_invite(data, datalen);
            break;
        case BURST_MSG_VOIP_ACCEPT:
            process_burst_voip_accept(data, datalen);
            break;
        case BURST_MSG_VOIP_HANGUP:
            process_burst_voip_hangup(data, datalen);
            break;
        case BURST_MSG_VOIP_RECORD_DATA: {
            burst_record_data* record_data = (burst_record_data*)data;
            // voip语音
            if (WLSDK_VOIP_ESTABLISH == s_burst_global.burst_voip.session_voip.state
                && record_data->sessionid == s_burst_global.burst_voip.session_voip.userid
                && record_data->sessiontype == WLSDK_SESSION_TYPE_SINGLE) {
                process_burst_voip_record_data(data, datalen);
            }
            break;
        }

        case BURST_MSG_VOIP_RECORD_END: {
            process_burst_voip_record_end(data, datalen);
            break;
        }
        case BURST_MSG_VOIP_PLAY_RESULT:
            process_burst_voip_play_result(data, datalen);
            break;
        case BURST_MSG_VOIP_PLAY_END:
            process_burst_voip_play_end(data, datalen);
            break;
        case BURST_MSG_BUTTON_PRESS:
            process_burst_button_press(data, datalen);
            break;
        case BURST_MSG_BUTTON_RELEASE:
            process_burst_button_release(data, datalen);
            break;
        case BURST_MSG_SET_VOIP_AUTO_ACCEPT:
            process_burst_set_voip_auto_accept(data, datalen);
            return 0;
        case BURST_MSG_CHECK_SESSION: {
            repo_session session;
            memset(&session, 0, sizeof(session));
            burst_check_active_session(&session);
            // 有会话清除,更新会话列表
            if (repo_remove_invalid_session()) {
                wl_core_send_message(WL_MODULE_SESSION, SESSION_MSG_GET_SESSIONS, wl_null, 0);
            }
            wl_core_send_message(WL_MODULE_SESSION, SESSION_MSG_CHECK_ATTACH_SESSION, wl_null, 0);
            return 0;
        }
        case BURST_MSG_CLEAR_ACCOUNT:
            s_burst_global.active_sessionid   = 0;
            s_burst_global.active_sessiontype = 0;
            s_burst_global.active_lifeleft    = 0;
            return 0;
        case BURST_MSG_RECORD_DROP_DURATION: {
            wl_uint32 duration = *((wl_uint32*)data);
            burst_media_set_record_drop_duration(duration);
            break;
        }
        case BURST_MSG_RECORD_END_DROP_DURATION: {
            wl_uint32 duration = *((wl_uint32*)data);
            burst_media_set_record_end_drop_duration(duration);
            break;
        }
        case BURST_MSG_BURST_SWITCH: {
            process_burst_switch();
            break;
        }
        default:
            WLLOGW("msgid %d not support", msgid);
            break;
    }
    burst_check();

    if (WL_INVALID_TIMER == s_burst_global.timer) {
        // 超时定时器
        s_burst_global.timer = wl_timer_start(
            BURST_CHECK_TIMER_INTERVAl, REPETITIVE, burst_timer_callback, wl_null, WL_TASK_APP, "burst_module");
        WLLOGI("timer %p", s_burst_global.timer);
        if (WL_INVALID_TIMER == s_burst_global.timer) {
            WLLOGW("wl_timer_start fail");
            return -1;
        }
    }
    return 0;
}

static wl_int32
    burst_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGT("message %d resultCode %d", message->message, message->resultCode);
    switch (message->message) {
        case WL_SESSION_MSGDATA_MSG:
            process_burst_msgdata_message(message, content, contentlen, context);
            break;
        case WL_SESSION_BURST_CTRL_MSG:
            process_burst_control_message(message, content, contentlen, context);
            break;
        default:
            break;
    }
    return 0;
}

static wl_int32 burst_protocol_client_listen_callback(PBWeilaMsg* message, void* context) {
    WLLOGD("message %d", message->message);
    switch (message->message) {
        case WL_SESSION_MSGDATA_MSG: {
            // 内存达到临界不处理消息
            if (WL_MEMORY_PTT_IS_THRESHOLD) {
                WLLOGW("ptt memory threshold fresh size %d", wl_mem_fresh_size());
                break;
            }
            process_burst_msgdata_notify_message(message, context);
            break;
        }
        case WL_SESSION_BURST_CTRL_MSG: {
            process_burst_control_notify_message(message, context);
            break;
        }
        case WL_SESSION_MONITOR_CTRL_MSG: {
            process_monitor_control_notify_message(message, context);
            break;
        }
        case WL_SESSION_VOIP_INVITE_MSG: {
            if (burst_media_is_voip_support()) {
                process_voip_invite_notify_message(message, context);
            }
            break;
        }
        case WL_SESSION_VOIP_ACCEPT_MSG: {
            process_voip_accept_notify_message(message, context);
            break;
        }
        case WL_SESSION_VOIP_HANGUP_MSG: {
            process_voip_hangup_notify_message(message, context);
            break;
        }
        default:
            break;
    }
    burst_check();

    if (WL_INVALID_TIMER == s_burst_global.timer) {
        // 超时定时器
        s_burst_global.timer = wl_timer_start(
            BURST_CHECK_TIMER_INTERVAl, REPETITIVE, burst_timer_callback, wl_null, WL_TASK_APP, "burst_module");
        WLLOGI("timer %p", s_burst_global.timer);
        if (WL_INVALID_TIMER == s_burst_global.timer) {
            WLLOGW("wl_timer_start fail");
            return 0;
        }
    }
    return 0;
}

static void burst_burst_meida_recorder_callback(wl_uint32 marker,
                                                wl_uint32 frameslen,
                                                wl_char*  frames,
                                                wl_uint32 framecount,
                                                wl_uint64 sessionid,
                                                wl_uint32 sessiontype,
                                                void*     context) {
    burst_record_data* record_data = (burst_record_data*)wl_malloc(sizeof(burst_record_data) + frameslen);
    if (!record_data) {
        WLLOGW("malloc fail");
        return;
    }
    record_data->marker      = marker;
    record_data->framecount  = framecount;
    record_data->sessionid   = sessionid;
    record_data->sessiontype = sessiontype;
    record_data->frameslen   = frameslen;
    record_data->context     = context;
    memcpy(record_data->frames, frames, frameslen);
    wl_core_send_message(WL_MODULE_BURST, BURST_MSG_RECORD_DATA, record_data, sizeof(burst_record_data) + frameslen);
    wl_free(record_data);
    WLLOGT("wl_core_send_message BURST_MSG_RECORD_DATA");
}

static void burst_burst_meida_recorder_end_callback(wl_int32 result, wl_bool isclosed, void* context) {
    burst_end_result end_result;
    end_result.resultcode = result;
    end_result.context    = context;
    wl_core_send_message(WL_MODULE_BURST, BURST_MSG_RECORD_END, &end_result, sizeof(burst_end_result));
    WLLOGI("wl_core_send_message BURST_MSG_RECORD_END");
}

static void burst_burst_media_player_callback(wl_uint32 framecount, void* context) {
    burst_play_result play_result;
    play_result.framecount = framecount;
    play_result.context    = context;
    wl_core_send_message(WL_MODULE_BURST, BURST_MSG_PLAY_RESULT, &play_result, sizeof(burst_play_result));
    WLLOGT("wl_core_send_message BURST_MSG_PLAY_RESULT");
}

static void burst_burst_media_player_end_callback(wl_int32 result, wl_bool isclosed, void* context) {
    burst_end_result end_result;
    end_result.resultcode = result;
    end_result.context    = context;
    end_result.isclosed   = isclosed;
    wl_core_send_message(WL_MODULE_BURST, BURST_MSG_PLAY_END, &end_result, sizeof(burst_end_result));
    WLLOGI("wl_core_send_message BURST_MSG_PLAY_END");
}

// 录音检查
static void burst_start_record(wl_uint64 sessionid, wl_int32 sessiontype, wl_int32 prority) {
    // 语音播放中,如果播放器优先级低于录音器,停止播放,如果优先级高于等于录音器,需要判断是否支持voip,不支持等语音播放完后再启动录音
    if (burst_media_is_playing()) {
        if (prority > burst_media_get_player_prority()) {
            WLLOGI("burst_stop_player");
        } else {
            if (!burst_media_is_voip_support()) {
                return;
            }
        }
    }
    // 会话没启动录音
    if (!burst_media_is_record_session(sessionid, sessiontype)) {
        burst_stop_player_immediately();
        // 录音中
        if (burst_media_is_recording()) {
            // 先停止录音
            burst_media_stop_recorder();
        }
        if (0
            == burst_media_start_recorder(burst_burst_meida_recorder_callback,
                                          burst_burst_meida_recorder_end_callback,
                                          sessionid,
                                          sessiontype,
                                          prority,
                                          BURST_PTT_PACKAGE_FRAMES,
                                          wl_null)) {
            // PTT讲话
            if (s_burst_global.burst_pressed) {
                burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_REQUEST, WLSDK_RESULT_SUCCESS, wl_null, 0);
            }
            WLLOGI("burst_media_start_recorder success");
        } else {
            // 话权请求失败释放按键要在stop recorder之前 不在发送录音
            s_burst_global.burst_pressed = wl_false;
            burst_media_stop_recorder();
            burst_stop_player();
            burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                                  burst_burst_media_player_end_callback,
                                  BURST_MEDIA_PRORITY_ALARM,
                                  0,
                                  (void*)wl_null);
            WLLOGW("burst_media_start_recorder fail");
        }
    }
}

static void burst_check_record(void) {
    burst_monitor* monitor = wl_list_get(&s_burst_global.monitor_list, 0);
    // 有监听
    if (monitor) {
        // 讲话中,停止讲话
        if (s_burst_global.burst_pressed) {
            // 先停止录音
            burst_stop_recorder(WLSDK_RESULT_SUCCESS);
            WLLOGI("burst_stop_player");
            // 停止播放器
            burst_stop_player();
            // 播放告警音
            burst_media_play_tone(BURST_MEDIA_TONE_BURST_STOP,
                                  burst_burst_media_player_end_callback,
                                  BURST_MEDIA_PRORITY_ALARM,
                                  0,
                                  (void*)wl_null);
            burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_RELEASE, WLSDK_RESULT_LOCAL_BURST_MONITOR, wl_null, 0);
        }
        burst_start_record(monitor->monitorid, WLSDK_SESSION_TYPE_SINGLE, BURST_MEDIA_PRORITY_MONITOR);
        // 录音器生命期比监听长1000秒,防止录音器先释放
        burst_media_refresh_recorder_lifetime(monitor->lifetime + 1000);
    }
    // 不需要监听
    else {
        // PTT讲话
        if (s_burst_global.burst_pressed) {
            burst_start_record(s_burst_global.burst_request.session.sessionid,
                               s_burst_global.burst_request.session.sessiontype,
                               BURST_MEDIA_PRORITY_RECORD);
        } else {
            burst_stop_recorder(WLSDK_RESULT_SUCCESS);
        }
    }
}

// 超时消息检查
static void burst_check_timeout_msg(void) {
    wl_list_iterator iterator;
    burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    // 检查超时消息
    while (!wl_list_iterator_is_end(&iterator)) {
        // 播放器超时
        if (0 >= msg->lifetime) {
            switch (msg->msgtype) {
                case BURST_MSGTYPE_PTT: {
                    burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                    wl_list_free(&pttmsg->package_list);
                    break;
                }
            }
            if (burst_media_is_playing() && (wl_uint32)burst_media_get_player_context() == msg->seq) {
                burst_idle(&msg->msgsession.session, WLSDK_RESULT_SUCCESS);
                WLLOGI("burst_stop_player seq %d", msg->seq);
                burst_stop_player();
                if (BURST_MSGTYPE_PTT == msg->msgtype) {
                    burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                    // PTT全语音STOP提示音在burst_media_set_player_frame_end时插入
                    if (2 != pttmsg->marker && 3 != pttmsg->marker) {
                        burst_media_play_tone(BURST_MEDIA_TONE_LISTEN_STOP,
                                              burst_burst_media_player_end_callback,
                                              BURST_MEDIA_PRORITY_ALARM,
                                              0,
                                              (void*)msg->seq);
                    }
                }
            }
            WLLOGI("wl_list_iterator_remove seq %d", msg->seq);
            burst_message_free(msg);
            msg = (burst_msg*)wl_list_iterator_remove(&iterator);
            continue;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
}

// 监听超时检查
static void burst_check_timeout_monitor(void) {
    wl_list_iterator iterator;
    burst_monitor*   monitor = (burst_monitor*)wl_list_get_first(&s_burst_global.monitor_list, &iterator);
    // 检查超时消息
    while (!wl_list_iterator_is_end(&iterator)) {
        // 监听超时
        if (0 >= monitor->lifetime) {
            WLLOGI("wl_list_iterator_remove monitorid %d", monitor->monitorid);
            if (1 == monitor->marker) {
                monitor->marker = 2;
                burst_monitor_send_monitor_frames(monitor, wl_null);
            }
            wl_free(monitor);
            monitor = (burst_monitor*)wl_list_iterator_remove(&iterator);
            continue;
        }
        monitor = (burst_monitor*)wl_list_get_next(&iterator);
    }
}

static void burst_check_synthesis_text(burst_msg* msg) {
    // 未合成PTT
    if (!tts_client_is_synthesis_ptt()) {
        // 文本消息未处理,文字转语音
        burst_textmsg* textmsg = (burst_textmsg*)msg->content;
        if (!textmsg->issynthesis) {
            tts_client_synthesis_ptt(textmsg->text,
                                     msg->msgsession.senderid,
                                     msg->msgsession.session.sessionid,
                                     msg->msgsession.session.sessiontype,
                                     msg->msgsession.sendername,
                                     msg->msgsession.session.name,
                                     TTS_CLIENT_REQUEST_TIMEOUT_60,
                                     burst_tts_client_callback,
                                     (void*)msg->seq);
            msg->lifetime        = BURST_MSG_LIFETIME_TEXT;
            msg->prority         = BURST_MEDIA_PRORITY_TEXT;
            textmsg->issynthesis = wl_true;
            // TEXT合成需要时间可以先播放其他消息
            wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CHECK, wl_null, 0);
        }
    }
}

static void burst_check_play_tts(burst_msg* msg) {
    burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
    // 定位上次播放位置
    wl_uint32 pos = 0;
    wl_uint32 idx = 0;

    // 消息数据非法
    if (sizeof(burst_audiomsg) + audiomsg->audiolen != msg->contentlen) {
        WLLOGW("msg seq %d invalid", msg->seq);
        burst_delete_message(msg->seq);
        return;
    }
    // 消息已播放完
    if (audiomsg->playcount >= audiomsg->framecount) {
        WLLOGW("msg seq %d play finished", msg->seq);
        burst_delete_message(msg->seq);
        return;
    }

    WLLOGI("burst_media_start_player");
    burst_stop_player();
    burst_media_start_player(
        burst_burst_media_player_callback, burst_burst_media_player_end_callback, msg->prority, (void*)msg->seq);
    for (; idx < audiomsg->playcount; idx++) {
        wl_uint16 framelen = 0;
        memcpy(&framelen, audiomsg->audio + pos, sizeof(wl_uint16));
        pos += sizeof(wl_uint16) + framelen;
    }
    // 消息生命期统一为BURST_MSG_LIFETIME_MAX方便统一管理回收
    //  msg->lifetime = BURST_AUDIO_MSG_WAIT_TIME + (audiomsg->framecount - audiomsg->playcount) * BURST_FRAME_TIME;
    burst_media_add_play_audio(
        audiomsg->audio + pos, audiomsg->audiolen - pos, audiomsg->framecount - audiomsg->playcount);
    burst_media_set_player_frame_end();
}

static void burst_check_play_audio(burst_msg* msg) {
    burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
    // 定位上次播放位置
    wl_uint32 pos = 0;
    wl_uint32 idx = 0;

    // 消息数据非法
    if (sizeof(burst_audiomsg) + audiomsg->audiolen != msg->contentlen) {
        WLLOGW("msg seq %d invalid", msg->seq);
        burst_delete_message(msg->seq);
        return;
    }
    // 消息已播放完
    if (audiomsg->playcount >= audiomsg->framecount) {
        WLLOGW("msg seq %d play finished", msg->seq);
        burst_delete_message(msg->seq);
        return;
    }

    burst_listen(&msg->msgsession.session,
                 audiomsg->playcount,
                 audiomsg->framecount,
                 msg->msgsession.senderid,
                 msg->msgsession.sendername);
    WLLOGI("burst_media_start_player seq %d", msg->seq);
    burst_stop_player();
    burst_media_play_tone(
        BURST_MEDIA_TONE_LISTEN_START, burst_burst_media_player_end_callback, msg->prority, 0, (void*)wl_null);
    burst_media_start_player(
        burst_burst_media_player_callback, burst_burst_media_player_end_callback, msg->prority, (void*)msg->seq);
    for (; idx < audiomsg->playcount; idx++) {
        wl_uint16 framelen = 0;
        memcpy(&framelen, audiomsg->audio + pos, sizeof(wl_uint16));
        pos += sizeof(wl_uint16) + framelen;
    }

    // 消息生命期统一为BURST_MSG_LIFETIME_MAX方便统一管理回收
    //  msg->lifetime = BURST_AUDIO_MSG_WAIT_TIME + (audiomsg->framecount - audiomsg->playcount) * BURST_FRAME_TIME;
    burst_media_add_play_audio(
        audiomsg->audio + pos, audiomsg->audiolen - pos, audiomsg->framecount - audiomsg->playcount);
    burst_media_play_tone(
        BURST_MEDIA_TONE_LISTEN_STOP, burst_burst_media_player_end_callback, msg->prority, 0, (void*)msg->seq);
}

static void burst_check_play_ptt(burst_msg* msg) {
    burst_pttmsg*     pttmsg     = (burst_pttmsg*)msg->content;
    wl_uint32         playcount  = pttmsg->playcount;
    wl_uint32         framecount = 0;
    wl_list_iterator  iterator;
    burst_pttpackage* pttpackage = wl_null;

    burst_listen(&msg->msgsession.session,
                 pttmsg->playcount,
                 pttmsg->framecount,
                 msg->msgsession.senderid,
                 msg->msgsession.sendername);
    WLLOGI("seq %d marker %d", msg->seq, pttmsg->marker);
    burst_stop_player();
    burst_media_play_tone(
        BURST_MEDIA_TONE_LISTEN_START, burst_burst_media_player_end_callback, msg->prority, 0, (void*)wl_null);
    burst_media_start_player(
        burst_burst_media_player_callback, burst_burst_media_player_end_callback, msg->prority, (void*)msg->seq);
    // 消息生命期统一为BURST_MSG_LIFETIME_MAX方便统一管理回收
    //  msg->lifetime = BURST_PTT_MSG_WAIT_TIME;

    // 将所有剩余未播放语音全部加载到media
    pttpackage = (burst_pttpackage*)wl_list_get_first(&pttmsg->package_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // 通过playcount定位播放位置
        if (playcount >= pttpackage->framecount) {
            playcount -= pttpackage->framecount;
        } else {
            // 当前package为续播package
            playcount = 0;
            burst_media_add_play_audio(pttpackage->frames, pttpackage->frameslen, pttpackage->framecount);
            framecount += pttpackage->framecount;
            // 消息生命期统一为BURST_MSG_LIFETIME_MAX方便统一管理回收
            //  msg->lifetime += (pttpackage->framecount * BURST_FRAME_TIME);
        }
        pttpackage = (burst_pttpackage*)wl_list_get_next(&iterator);
    }
    // PTT结束帧
    if (2 == pttmsg->marker || 3 == pttmsg->marker) {
        burst_media_play_tone(
            BURST_MEDIA_TONE_LISTEN_STOP, burst_burst_media_player_end_callback, msg->prority, 0, (void*)msg->seq);
    }
}

static wl_bool burst_is_session_message_allow_play(burst_msg* msg) {
    // 微喇即时消息且当前会话还在保护期内且非当前会话
    if (msg->prority < BURST_MEDIA_PRORITY_TTS && (s_burst_global.active_lifeleft > 0)
        && 0 != msg->msgsession.session.sessionid
        && (msg->msgsession.session.sessionid != s_burst_global.active_sessionid
            || msg->msgsession.session.sessiontype != s_burst_global.active_sessiontype)) {
        return wl_false;
    }
    return wl_true;
}

static void burst_check_play_msg(void) {
    burst_msg*   proritymsg = burst_get_prority_message();
    repo_session active_session;
    if (wl_null == proritymsg) {
        WLLOGI("no msg need play");
        return;
    }
    // 语音录音中,如果录音器优先级低于等于播放器,如果支持voip,录音播放同时进行,如果不支持voip,停止录音,优先播放
    if (burst_media_is_recording()) {
        if (burst_media_get_recorder_prority() <= proritymsg->prority) {
            // 不支持voip,停止录音
            if (!burst_media_is_voip_support()) {
                WLLOGI("prority msg prority %d recorder prority %d",
                       proritymsg->prority,
                       burst_media_get_recorder_prority());
                burst_stop_recorder(WLSDK_RESULT_SUCCESS);
            }
        } else {
            WLLOGI(
                "prority msg prority %d recorder prority %d", proritymsg->prority, burst_media_get_recorder_prority());
            return;
        }
    }
    // 检查是否允许播放会话消息
    if (!burst_is_session_message_allow_play(proritymsg)) {
        WLLOGI("session message not allow play");
        return;
    }

    // 在播放消息
    if (burst_media_is_playing()) {
        // 消息优先
        if (proritymsg->prority > burst_media_get_player_prority()) {
            WLLOGI("burst_stop_player");
            burst_stop_player();
        } else {
            // 会话优先
            burst_msg* msg = burst_get_message((wl_uint32)burst_media_get_player_context());
            if (msg) {
                wl_int32 msgsession_prority =
                    repo_get_session_prority(msg->msgsession.session.sessionid, msg->msgsession.session.sessiontype);
                wl_int32 proritymsgsession_prority = repo_get_session_prority(
                    proritymsg->msgsession.session.sessionid, proritymsg->msgsession.session.sessiontype);
                if (proritymsgsession_prority > msgsession_prority) {
                    WLLOGI("burst_stop_player");
                    burst_stop_player();
                } else {
                    WLLOGI(
                        "player prority %d higher than msg %d", burst_media_get_player_prority(), proritymsg->prority);
                    return;
                }
            } else {
                WLLOGI("player prority %d higher than msg %d", burst_media_get_player_prority(), proritymsg->prority);
                return;
            }
        }
    }
    if (WLSDK_SESSION_TYPE_GROUP == proritymsg->msgsession.session.sessiontype) {
        // 正式群只播放attach群消息
        if (repo_is_normal_group(proritymsg->msgsession.session.sessionid)
            && REPO_CLIENT_EMBEDDED_ATTACH == repo_get_extension_config_client_type()) {
            if (!repo_is_session_default(proritymsg->msgsession.session.sessionid,
                                         proritymsg->msgsession.session.sessiontype)) {
                WLLOGW("session %llu type %d not attach",
                       proritymsg->msgsession.session.sessionid,
                       proritymsg->msgsession.session.sessiontype);
                // 删除未attach群消息
                burst_delete_message(proritymsg->seq);
                wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CHECK, wl_null, 0);
                return;
            }
        }
    }
    // 内存临界状态丢弃文字消息
    if (BURST_MSGTYPE_TEXT == proritymsg->msgtype) {
        if (WL_MEMORY_PTT_IS_THRESHOLD) {
            WLLOGW("ptt memory threshold drop text message fresh size %d", wl_mem_fresh_size());
            burst_delete_message(proritymsg->seq);
            wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CHECK, wl_null, 0);
            return;
        }
    }
    if (0
        != repo_get_session(
            proritymsg->msgsession.session.sessionid, proritymsg->msgsession.session.sessiontype, &active_session)) {
        memset(&active_session, 0, sizeof(repo_session));
        memcpy(&active_session.session, &proritymsg->msgsession.session, sizeof(wlsdk_session));
    }
    WLLOGD("session %lld type %d msgtype %d",
           active_session.session.sessionid,
           active_session.session.sessiontype,
           proritymsg->msgtype);
    burst_set_active_session(&active_session);

    // 个人设备
    if (!repo_is_corp_user()) {
        // 临时群,同步群名
        if (WLSDK_SESSION_TYPE_GROUP == proritymsg->msgsession.session.sessiontype) {
            repo_group group;
            if (0 != repo_get_group(proritymsg->msgsession.session.sessionid, &group)) {
                repo_session session;
                if (0
                    == repo_get_session(proritymsg->msgsession.session.sessionid,
                                        proritymsg->msgsession.session.sessiontype,
                                        &session)) {
                    // 防止重复请求,60秒内只允许同步一次
                    if (0 != session.sync_timestamp && repo_get_server_timestamp() > session.sync_timestamp + 60) {
                        group_sync_ids syncids;
                        syncids.count  = 1;
                        syncids.ids[0] = session.session.sessionid;
                        wl_core_send_message(
                            WL_MODULE_GROUP, GROUP_MSG_SYNC_GROUP_INFO, &syncids, sizeof(group_sync_ids));
                        repo_update_session_sync_timestamp(proritymsg->msgsession.session.sessionid,
                                                           proritymsg->msgsession.session.sessiontype,
                                                           repo_get_server_timestamp());
                    }
                }
            }
        }
    }

    switch (proritymsg->msgtype) {
        case BURST_MSGTYPE_TEXT:
            burst_check_synthesis_text(proritymsg);
            break;
        case BURST_MSGTYPE_TTS:
            WLLOGI("burst_stop_player");
            burst_check_play_tts(proritymsg);
            break;
        // 语音消息
        case BURST_MSGTYPE_AUDIO: {
            WLLOGI("burst_check_play_audio");
            burst_check_play_audio(proritymsg);
            // 刷新会话
            repo_refresh_session_burst(proritymsg->msgsession.session.sessionid,
                                       proritymsg->msgsession.session.sessiontype);
            break;
        }
        // 回放
        case BURST_MSGTYPE_PLAYBACK: {
            WLLOGI("burst_check_play_audio");
            burst_check_play_audio(proritymsg);
            break;
        }
        // PTT消息
        case BURST_MSGTYPE_PTT: {
            WLLOGI("burst_stop_player");
            // 刷新会话
            repo_refresh_session_burst(proritymsg->msgsession.session.sessionid,
                                       proritymsg->msgsession.session.sessiontype);
            burst_check_play_ptt(proritymsg);
            break;
        }
        default:
            burst_delete_message(proritymsg->seq);
            wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CHECK, wl_null, 0);
            break;
    }
}

static void burst_listen_callback(void) {
    wl_list_iterator iterator;
    wl_uint32        seq = (wl_uint32)burst_media_get_player_context();
    burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (seq == msg->seq) {
            switch (msg->msgtype) {
                    // 回放、语音消息
                case BURST_MSGTYPE_AUDIO: {
                    burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
                    repo_refresh_session_burst(msg->msgsession.session.sessionid, msg->msgsession.session.sessiontype);
                    burst_listen(&msg->msgsession.session,
                                 audiomsg->playcount,
                                 audiomsg->framecount,
                                 msg->msgsession.senderid,
                                 msg->msgsession.sendername);
                    break;
                }
                case BURST_MSGTYPE_PLAYBACK: {
                    burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
                    burst_listen(&msg->msgsession.session,
                                 audiomsg->playcount,
                                 audiomsg->framecount,
                                 msg->msgsession.senderid,
                                 msg->msgsession.sendername);
                    break;
                }
                case BURST_MSGTYPE_PTT: {
                    burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                    repo_refresh_session_burst(msg->msgsession.session.sessionid, msg->msgsession.session.sessiontype);
                    burst_listen(&msg->msgsession.session,
                                 pttmsg->playcount,
                                 pttmsg->framecount,
                                 msg->msgsession.senderid,
                                 msg->msgsession.sendername);
                    break;
                }
                default:
                    break;
            }
            break;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
}

static void burst_check(void) {
    // 消息超时检查
    burst_check_timeout_msg();
    // 监听超时检查
    burst_check_timeout_monitor();
    if (!s_burst_global.force_pause || s_burst_global.force_lifeleft < 0) {
        // 录音检查
        burst_check_record();
        // 检查待播放对列
        burst_check_play_msg();
    } else {
        WLLOGI("burst force pause lifeleft %d", s_burst_global.force_lifeleft);
    }
}

static wl_int32 add_monitor(burst_monitor* monitor) {
    burst_monitor* monitorinfo = (burst_monitor*)wl_malloc(sizeof(burst_monitor));
    if (monitorinfo) {
        memcpy(monitorinfo, monitor, sizeof(burst_monitor));
        wl_list_add(&s_burst_global.monitor_list, monitorinfo, -1);
    }
    return 0;
}

static wl_int32 get_monitor(wl_uint32 monitorid, burst_monitor* monitor) {
    wl_list_iterator iterator;
    burst_monitor*   monitorinfo = wl_list_get_first(&s_burst_global.monitor_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (monitorinfo->monitorid == monitorid) {
            memcpy(monitor, monitorinfo, sizeof(burst_monitor));
            return 0;
        }
        monitor = wl_list_get_next(&iterator);
    }
    return -1;
}

static wl_int32 update_monitor(burst_monitor* monitor) {
    wl_list_iterator iterator;
    burst_monitor*   monitorinfo = wl_list_get_first(&s_burst_global.monitor_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (monitorinfo->monitorid == monitor->monitorid) {
            memcpy(monitorinfo, monitor, sizeof(burst_monitor));
            return 0;
        }
        monitor = wl_list_get_next(&iterator);
    }
    return -1;
}

static wl_int32 delete_monitor(wl_uint32 monitorid) {
    wl_list_iterator iterator;
    burst_monitor*   monitorinfo = wl_list_get_first(&s_burst_global.monitor_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (monitorinfo->monitorid == monitorid) {
            wl_free(monitorinfo);
            wl_list_iterator_remove(&iterator);
            return 0;
        }
        monitorinfo = wl_list_get_next(&iterator);
    }
    return -1;
}

static void burst_monitor_send_monitor_frames(burst_monitor* monitor, PBLinkList* framelist) {
    PBBurstData   burstData;
    repo_user     user;
    repo_hardware hardware;
    PBEncResult   enc_result;
    wlsdk_session session;
    wl_int32      resultcode = WLSDK_RESULT_FAILURE;

    repo_get_selfinfo(&user);
    repo_get_hardware(&hardware);
    memset(&burstData, 0, sizeof(PBBurstData));

    burstData.sessionId   = monitor->monitorid;
    burstData.sessionType = repo_get_session_message_session_type(monitor->monitorid, WLSDK_SESSION_TYPE_SINGLE);
    burstData.senderId    = user.user.userid;
    burstData.sourceType  = hardware.source_type;
    burstData.burstSeq    = monitor->seq;
    burstData.packetSeq   = monitor->packageSeq;
    burstData.isMonitor   = wl_true;
    burstData.marker      = monitor->marker;
    burstData.pFrameList  = framelist;
    enc_result            = weilapb_buildSendBurstData(&burstData);
    WLLOGD("burst data session %lld seq %d marker %d framelist %p size %d",
           burstData.sessionId,
           burstData.burstSeq,
           monitor->marker,
           framelist,
           enc_result.encodedDataSize);
    session.sessionid   = monitor->monitorid;
    session.sessiontype = WLSDK_SESSION_TYPE_SINGLE;
    resultcode          = protocol_client_send_request(&enc_result,
                                              &session,
                                              sizeof(session),
                                              PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                              burst_protocol_client_request_callback,
                                              (void*)monitor->seq);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
    }
}

static void burst_monitor_start(wl_uint32 monitorid, wl_uint32 duration) {
    burst_monitor monitor;

    // 更新
    if (0 == get_monitor(monitorid, &monitor)) {
        monitor.lifetime = duration * 1000;
        update_monitor(&monitor);
    }
    // 新增
    else {
        // 暂时只支持一路监听
        // 清除监听用户
        wl_list_free(&s_burst_global.monitor_list);

        memset(&monitor, 0, sizeof(burst_monitor));
        monitor.monitorid  = monitorid;
        monitor.lifetime   = duration * 1000;
        monitor.packageSeq = 0;
        monitor.marker     = 0;
        monitor.seq        = ++s_burst_global.burst_seq;
        add_monitor(&monitor);

        burst_check_record();
    }
}

static void burst_monitor_stop(wl_uint32 monitorid) {
    burst_monitor monitor;

    // 更新
    if (0 == get_monitor(monitorid, &monitor)) {
        if (1 == monitor.marker) {
            monitor.marker = 2;
            burst_monitor_send_monitor_frames(&monitor, wl_null);
        }
        delete_monitor(monitorid);
    }
}

static void burst_ptt_request(void) {
    repo_session session;

    // 监听中
    if (wl_list_size(&s_burst_global.monitor_list) > 0) {
        burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_REQUEST, WLSDK_RESULT_LOCAL_BURST_MONITOR, wl_null, 0);
        // 先停止录音
        burst_media_stop_recorder();
        // 先重置播放
        burst_media_stop_player();
        // 播放告警音
        burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_RECORD,
                              0,
                              (void*)wl_null);
        return;
    }
    // 先停止上次录音
    burst_media_stop_recorder();

    if (WL_INVALID_TIMER != s_burst_global.burst_delay_timer) {
        wl_timer_stop(s_burst_global.burst_delay_timer);
        s_burst_global.burst_delay_timer = WL_INVALID_TIMER;
    }
    // 内存达到临界不录音
    if (WL_MEMORY_PTT_IS_THRESHOLD) {
        WLLOGW("ptt memory threshold fresh size %d", wl_mem_fresh_size());
        burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_REQUEST, WLSDK_RESULT_LOCAL_OUT_OF_MEMORY, wl_null, 0);
        // 先重置播放
        burst_media_stop_player();
        // 播放告警音
        burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_RECORD,
                              0,
                              (void*)wl_null);
        return;
    }
    memset(&session, 0, sizeof(session));
    // 空会话
    if (-1 == burst_check_active_session(&session)) {
        WLLOGW("current session is null");
        burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_REQUEST, WLSDK_RESULT_LOCAL_SESSION_INEXISTENT, wl_null, 0);
        // 先重置播放
        burst_media_stop_player();
        burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_RECORD,
                              0,
                              (void*)wl_null);
        return;
    }
    WLLOGD("burst request session id %lld type %d name %s",
           session.session.sessionid,
           session.session.sessiontype,
           session.session.name);
    s_burst_global.burst_request.totalcount = 0;
    s_burst_global.burst_request.resultcode = 0;
    memcpy(&s_burst_global.burst_request.session, &session.session, sizeof(wlsdk_session));
    s_burst_global.burst_pressed = wl_true;
    // 刷新会话
    repo_refresh_session_burst(s_burst_global.burst_request.session.sessionid,
                               s_burst_global.burst_request.session.sessiontype);
    burst_talk(&s_burst_global.burst_request.session, 0);
    // 先重置播放
    burst_media_stop_player();
    burst_media_play_tone(BURST_MEDIA_TONE_BURST_START,
                          burst_burst_media_player_end_callback,
                          BURST_MEDIA_PRORITY_RECORD + 1,
                          0,
                          (void*)wl_null);
    burst_check_record();
}

static void process_burst_request(void* data, wl_int32 datalen) {
    burst_ptt_request();
}

static void burst_ptt_release(void) {
    repo_session session;

    if (!s_burst_global.burst_pressed) {
        WLLOGD("burst pressed release");
        burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_RELEASE, WLSDK_RESULT_SUCCESS, wl_null, 0);
        burst_idle(&s_burst_global.burst_request.session, WLSDK_RESULT_SUCCESS);
        return;
    }
    // 监听中
    if (wl_list_size(&s_burst_global.monitor_list) > 0) {
        WLLOGD("monitor processing");
        burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_RELEASE, WLSDK_RESULT_SUCCESS, wl_null, 0);
        // 按键释放一定在stop recorder之后,因为stop record要发送录音尾音
        s_burst_global.burst_pressed = wl_false;
        burst_idle(&s_burst_global.burst_request.session, WLSDK_RESULT_SUCCESS);
        return;
    }
    // 停止录音
    burst_media_stop_recorder();
    // 按键释放一定在stop recorder之后,因为stop record要发送录音尾音
    s_burst_global.burst_pressed = wl_false;

    memset(&session, 0, sizeof(session));
    // 空会话
    if (-1 == burst_check_active_session(&session)) {
        WLLOGW("current session is null");
        burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_RELEASE, WLSDK_RESULT_LOCAL_SESSION_INEXISTENT, wl_null, 0);
        burst_media_play_tone(BURST_MEDIA_TONE_ALARM,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_ALARM,
                              0,
                              (void*)wl_null);
        burst_idle(&s_burst_global.burst_request.session, WLSDK_RESULT_SUCCESS);
        return;
    }
    burst_media_stop_player();
    burst_media_play_tone(BURST_MEDIA_TONE_BURST_STOP,
                          burst_burst_media_player_end_callback,
                          BURST_MEDIA_PRORITY_RECORD,
                          0,
                          (void*)wl_null);
    burst_idle(&s_burst_global.burst_request.session, WLSDK_RESULT_SUCCESS);
    WLLOGD("burst release session %s", session.session.name);
    burst_sdk_message_callback(WLSDK_MSG_SESSION_BURST_RELEASE, s_burst_global.burst_request.resultcode, wl_null, 0);
    s_burst_global.active_lifeleft = 5000;  // 会话释放话权5秒保护期
    // 回听
    //  process_burst_playback(NULL, 0);
}

static void burst_delay_timer_callback(WL_TIMER timer, void* context) {
    burst_ptt_release();
}

wl_int32 burst_module_burst_release_audio_drop(void) {
    return burst_media_stop_recorder_audio();
}

static void process_burst_release(void* data, wl_int32 datalen) {
    if (WL_INVALID_TIMER != s_burst_global.burst_delay_timer) {
        wl_timer_stop(s_burst_global.burst_delay_timer);
        s_burst_global.burst_delay_timer = WL_INVALID_TIMER;
    }
// 延时释放
#if 1
    s_burst_global.burst_delay_timer = wl_timer_start(
        BURST_DELAY_TIMER_INTERVAl, NON_REPETITIVE, burst_delay_timer_callback, wl_null, WL_TASK_APP, "burst_release");
    WLLOGI("timer %p", s_burst_global.timer);
    if (WL_INVALID_TIMER == s_burst_global.burst_delay_timer) {
        WLLOGW("wl_timer_start fail");
        burst_ptt_release();
        return;
    }
#else
    burst_ptt_release();
#endif
}

static void process_burst_playback(void* data, wl_int32 datalen) {
    wl_time         current_time = repo_get_server_timestamp();
    repo_playback*  playback     = wl_null;
    burst_msg*      msg          = wl_null;
    burst_audiomsg* audiomsg     = wl_null;
    // 录音中不允许回放
    if (s_burst_global.burst_pressed) {
        burst_sdk_message_callback(WLSDK_MSG_SESSION_PLAYBACK, WLSDK_RESULT_LOCAL_TALKING, wl_null, 0);
        return;
    }
    // 监听中且监听优先与回放
    if (wl_list_size(&s_burst_global.monitor_list) > 0 && BURST_MEDIA_PRORITY_MONITOR > BURST_MEDIA_PRORITY_PLAYBACK) {
        burst_sdk_message_callback(WLSDK_MSG_SESSION_PLAYBACK, WLSDK_RESULT_LOCAL_BURST_MONITOR, wl_null, 0);
        return;
    }

    if (burst_media_is_playing()) {
        wl_uint32        seq = (wl_uint32)burst_media_get_player_context();
        wl_list_iterator iterator;
        burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            if (msg->seq == seq) {
                // 回放中,播放下一条
                if (BURST_MSGTYPE_PLAYBACK == msg->msgtype) {
                    s_burst_global.playback_idx++;
                    break;
                }
            }
            msg = (burst_msg*)wl_list_get_next(&iterator);
        }
        // 回放重置
        if (!msg && (current_time > s_burst_global.playback_finish_timestamp + PLAYBACK_IDX_RESET_INTERVAL)) {
            s_burst_global.playback_idx = 0;
        }
    }
    // 回放重置
    else if (current_time > s_burst_global.playback_finish_timestamp + PLAYBACK_IDX_RESET_INTERVAL) {
        s_burst_global.playback_idx = 0;
    }
    // 重置时间内,播放下一条
    else {
        s_burst_global.playback_idx++;
    }
    s_burst_global.playback_finish_timestamp = current_time;
    playback                                 = repo_get_burst_playback(s_burst_global.playback_idx);
    // 没有合适的回放记录
    if (wl_null == playback) {
        burst_media_play_tone(BURST_MEDIA_TONE_ALARM,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_ALARM,
                              0,
                              (void*)wl_null);
        burst_sdk_message_callback(WLSDK_MSG_SESSION_PLAYBACK, WLSDK_RESULT_LOCAL_PLAYBACK_TO_BOTTOM, wl_null, 0);
        return;
    }
    // 会话消息唯一
    burst_clean_message_by_msgtype(BURST_MSGTYPE_PLAYBACK);
    msg = (burst_msg*)wl_malloc(sizeof(burst_msg) + sizeof(burst_audiomsg) + playback->audiolen);
    if (wl_null == msg) {
        wl_free(playback);
        burst_media_play_tone(BURST_MEDIA_TONE_ALARM,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_ALARM,
                              0,
                              (void*)wl_null);
        burst_sdk_message_callback(WLSDK_MSG_SESSION_PLAYBACK, WLSDK_RESULT_LOCAL_OUT_OF_MEMORY, wl_null, 0);
        return;
    }
    msg->seq        = ++s_burst_global.msg_seq;
    msg->msgtype    = BURST_MSGTYPE_PLAYBACK;
    msg->prority    = BURST_MEDIA_PRORITY_PLAYBACK;
    msg->alloccount = sizeof(burst_msg) + sizeof(burst_audiomsg) + playback->audiolen;
    msg->contentlen = sizeof(burst_audiomsg) + playback->audiolen;
    audiomsg        = (burst_audiomsg*)msg->content;
    memcpy(&msg->msgsession.session, &playback->session, sizeof(wlsdk_session));
    msg->msgsession.senderid = playback->senderid;
    strncpy(msg->msgsession.sendername, playback->sendername, sizeof(msg->msgsession.sendername));
    msg->msgsession.sendername[sizeof(msg->msgsession.sendername) - 1] = '\0';
    msg->msgsession.msgid                                              = playback->msgid;
    msg->msgsession.created                                            = playback->created;
    // msg->lifetime        = audiomsg->framecount * BURST_FRAME_TIME + BURST_AUDIO_MSG_WAIT_TIME;
    msg->lifetime        = BURST_MSG_LIFETIME_PLAYBACK;
    audiomsg->playcount  = 0;
    audiomsg->framecount = playback->framecount;
    audiomsg->audiolen   = playback->audiolen;
    memcpy(audiomsg->audio, playback->audio, playback->audiolen);
    if (-1 == wl_list_add(&s_burst_global.msg_list, msg, 0)) {
        wl_free(playback);
        burst_message_free(msg);
        burst_media_play_tone(BURST_MEDIA_TONE_ALARM,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_ALARM,
                              0,
                              (void*)wl_null);
        burst_sdk_message_callback(WLSDK_MSG_SESSION_PLAYBACK, WLSDK_RESULT_LOCAL_OUT_OF_MEMORY, wl_null, 0);
        return;
    }
    wl_free(playback);
    WLLOGI("burst_stop_player");
    // 停止播放器
    burst_stop_player();
}

static void process_burst_tts(void* data, wl_int32 datalen) {
    wl_char*  audio    = wl_null;
    wl_uint32 audiolen = 0;

    burst_clean_message_by_msgtype(BURST_MSGTYPE_TTS);
    // tts有缓存
    if (0 == repo_get_tts_audio(data, strlen(data), &audio, &audiolen)) {
        burst_process_tts_audio(wl_null, TTS_TTS, audio, audiolen, wl_null);
        wl_free(audio);
    } else {
        tts_client_synthesis_tts(data, TTS_CLIENT_REQUEST_TIMEOUT_15, burst_tts_client_callback, wl_null);
    }
}

static void process_burst_realtime_audio(void* data, wl_int32 datalen) {
    burst_realtime_audio* msgdata = (burst_realtime_audio*)data;

    if (wl_null == data || (sizeof(burst_realtime_audio) + msgdata->datalen) > datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        return;
    }

    burst_clean_message_by_msgtype(BURST_MSGTYPE_REALTIME);

    // 录音中不允许回放
    if (s_burst_global.burst_pressed) {
        burst_sdk_message_callback(WLSDK_MSG_SESSION_PLAYBACK, WLSDK_RESULT_LOCAL_TALKING, wl_null, 0);
        WLLOGI("talking");
        return;
    }
    // 监听中且监听优先于回放
    if (wl_list_size(&s_burst_global.monitor_list) > 0 && BURST_MEDIA_PRORITY_MONITOR > BURST_MEDIA_PRORITY_REALTIME) {
        burst_sdk_message_callback(WLSDK_MSG_SESSION_PLAYBACK, WLSDK_RESULT_LOCAL_BURST_MONITOR, wl_null, 0);
        WLLOGI("monitor");
        return;
    }
    if (msgdata->rate == 8000 || msgdata->rate == 16000) {
        if (msgdata->iscompress) {
            burst_process_tts_pcm_compress(msgdata->rate, msgdata->data, msgdata->datalen);
        } else {
            burst_process_tts_pcm(msgdata->rate, msgdata->data, msgdata->datalen);
        }
    } else {
        burst_process_tts_audio(wl_null, TTS_TTS, msgdata->data, msgdata->datalen, wl_null);
    }
    WLLOGI("burst_tts_client_callback");
}

static void process_burst_record_data(void* data, wl_int32 datalen) {
    repo_hardware      hardware;
    burst_record_data* record_data = (burst_record_data*)data;
    PBLinkList         framelist;
    wl_uint32          pos        = 0;
    wl_uint32          idx        = 0;
    wl_int32           resultcode = WLSDK_RESULT_FAILURE;
    PBEncResult        enc_result;

    s_burst_global.active_lifeleft = 5000;  // 会话录音中话权5秒保护期
    if (0 == record_data->marker || 3 == record_data->marker) {
        repo_user user;
        // 录音时间过短
        if (3 == record_data->marker && 5 > record_data->framecount) {
            burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                                  burst_burst_media_player_end_callback,
                                  BURST_MEDIA_PRORITY_ALARM,
                                  0,
                                  (void*)wl_null);
            return;
        }
        repo_get_selfinfo(&user);
        weilapb_startPttAudio(record_data->sessionid,
                              repo_get_session_message_session_type(record_data->sessionid, record_data->sessiontype),
                              user.user.userid);
    }
    s_burst_global.burst_request.totalcount += record_data->framecount;
    memset(&framelist, 0, sizeof(PBLinkList));
    for (idx = 0; idx < record_data->framecount; idx++) {
        wl_uint16 framelen = 0;
        wl_char*  data     = wl_null;
        memcpy(&framelen, record_data->frames + pos, sizeof(wl_uint16));
        pos += sizeof(wl_uint16);
        data = record_data->frames + pos;
        burst_pblink_add_tail(&framelist, data, framelen);
        pos += framelen;
    }
    repo_get_hardware(&hardware);
    enc_result = weilapb_buildSendPttFrames(record_data->marker, hardware.source_type, &framelist);
    WLLOGI("burst data size %d", enc_result.encodedDataSize);
    resultcode = protocol_client_send_request(&enc_result,
                                              &s_burst_global.burst_request.session,
                                              sizeof(s_burst_global.burst_request.session),
                                              PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                              burst_protocol_client_request_callback,
                                              wl_null);
    burst_pblink_free(&framelist);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
        burst_stop_recorder(resultcode);
        burst_media_play_tone(
            BURST_MEDIA_TONE_DENY, burst_burst_media_player_end_callback, BURST_MEDIA_PRORITY_ALARM, 0, (void*)wl_null);
        return;
    }
}

static void process_monitor_record_data(void* data, wl_int32 datalen) {
    if (wl_list_size(&s_burst_global.monitor_list) > 0) {
        burst_record_data* record_data = (burst_record_data*)data;
        PBLinkList         framelist;
        wl_uint32          pos = 0;
        wl_uint32          idx = 0;
        wl_list_iterator   iterator;
        burst_monitor*     monitor = wl_null;

        memset(&framelist, 0, sizeof(framelist));
        for (idx = 0; idx < record_data->framecount; idx++) {
            wl_uint16 framelen = 0;
            wl_char*  data     = wl_null;
            memcpy(&framelen, record_data->frames + pos, sizeof(wl_uint16));
            pos += sizeof(wl_uint16);
            data = record_data->frames + pos;
            burst_pblink_add_tail(&framelist, data, framelen);
            pos += framelen;
        }

        monitor = (burst_monitor*)wl_list_get_first(&s_burst_global.monitor_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            // 监听会话为当前对讲会话
            if (s_burst_global.burst_pressed && monitor->monitorid == s_burst_global.burst_request.session.sessionid
                && WLSDK_SESSION_TYPE_SINGLE == s_burst_global.burst_request.session.sessiontype) {
                // 如果有监听语音未完成,发送结束帧
                if (0 != monitor->marker) {
                    monitor->marker = 2;
                    burst_monitor_send_monitor_frames(monitor, &framelist);
                    // 重置帧数据为首帧
                    monitor->marker     = 0;
                    monitor->packageSeq = 0;
                    monitor->seq++;
                }
            } else {
                // 首帧
                if (0 == monitor->marker) {
                    if (2 == record_data->marker || 3 == record_data->marker) {
                        monitor->marker = 3;
                    }
                } else {
                    monitor->marker = record_data->marker;
                }
                burst_monitor_send_monitor_frames(monitor, &framelist);
                // 结束帧重置帧数据为首帧
                if (2 == record_data->marker || 3 == record_data->marker) {
                    monitor->marker     = 0;
                    monitor->packageSeq = 0;
                    monitor->seq++;
                }
                // 中间帧
                else {
                    monitor->marker = 1;
                }
            }
            monitor = (burst_monitor*)wl_list_get_next(&iterator);
        }
        burst_pblink_free(&framelist);
    }
}

static void process_burst_record_end(void* data, wl_int32 datalen) {
    burst_end_result* end_result = (burst_end_result*)data;

    WLLOGI("process_burst_record_end");
    if (end_result->resultcode != WLSDK_RESULT_SUCCESS) {
        burst_media_play_tone(
            BURST_MEDIA_TONE_DENY, burst_burst_media_player_end_callback, BURST_MEDIA_PRORITY_ALARM, 0, (void*)wl_null);
    }
    if (s_burst_global.burst_pressed) {
        burst_idle(&s_burst_global.burst_request.session, end_result->resultcode);
        s_burst_global.burst_pressed = wl_false;
    }
}

static void process_burst_play_result(void* data, wl_int32 datalen) {
    burst_play_result* play_result = (burst_play_result*)data;
    wl_list_iterator   iterator;
    burst_msg*         msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLLOGI("msg %d play_result msg %d msg type %d", msg->seq, play_result->context, msg->msgtype);
        if (msg->seq == (wl_uint32)(play_result->context)) {
            switch (msg->msgtype) {
                case BURST_MSGTYPE_AUDIO:
                case BURST_MSGTYPE_PLAYBACK: {
                    burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
                    audiomsg->playcount += play_result->framecount;
                    break;
                }
                case BURST_MSGTYPE_PTT: {
                    burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                    pttmsg->playcount += play_result->framecount;
                    break;
                }
                default:
                    break;
            }
            WLLOGI("wl_list_iterator_remove seq %d", msg->seq);
            break;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
}

static void process_burst_play_end(void* data, wl_int32 datalen) {
    burst_end_result* end_result = (burst_end_result*)data;
    wl_list_iterator  iterator;
    burst_msg*        msg = wl_null;

    // 播放中
    if (burst_media_get_player_context() == end_result->context && !end_result->isclosed) {
        WLLOGI("burst_stop_player");
        burst_stop_player();
    }

    msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLLOGI("msg %d play_result msg %d msg type %d", msg->seq, end_result->context, msg->msgtype);
        if (msg->seq == (wl_uint32)(end_result->context)) {
            wl_bool isfinished = wl_false;
            // 删除播放异常,删除
            if (WLSDK_RESULT_SUCCESS != end_result->resultcode) {
                isfinished = wl_true;
            } else {
                switch (msg->msgtype) {
                    case BURST_MSGTYPE_TTS:
                        // TTS打断不续播
                        isfinished = wl_true;
                        break;
                    // 语音、回放消息被打断
                    case BURST_MSGTYPE_AUDIO:
                    case BURST_MSGTYPE_PLAYBACK: {
                        burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
                        // 播放完成
                        if (audiomsg->playcount >= audiomsg->framecount) {
                            audiomsg->playcount = audiomsg->framecount;
                            isfinished          = wl_true;
                        } else {
                            // 语音小于2秒被打断,从头播放一次
                            if (audiomsg->framecount <= 2 * BURST_SECOND_FRAMES) {
                                audiomsg->playcount = 0;
                            }
                        }
                        break;
                    }
                    // PTT被打断记录打断位置
                    case BURST_MSGTYPE_PTT: {
                        burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                        WLLOGI("playcount %d play framecount %d marker %d",
                               pttmsg->playcount,
                               pttmsg->framecount,
                               pttmsg->marker);
                        // PTT接受完成
                        if (2 == pttmsg->marker || 3 == pttmsg->marker) {
                            if (pttmsg->playcount >= pttmsg->framecount) {
                                pttmsg->playcount = pttmsg->framecount;
                                isfinished        = wl_true;
                            }  // 语音小于2秒被打断,从头播放一次
                            if (pttmsg->framecount <= 2 * BURST_SECOND_FRAMES) {
                                pttmsg->playcount = 0;
                            }
                        }
                        // PTT未接收完
                        else {
                            // 未接收完PTT消息只等待BURST_PTT_MSG_WAIT_TIME
                            if (BURST_PTT_MSG_WAIT_TIME < msg->lifetime) {
                                msg->lifetime = BURST_PTT_MSG_WAIT_TIME;
                            }
                            // 播放器正常结束,播放结束音
                            if (pttmsg->playcount >= pttmsg->framecount) {
                                burst_media_play_tone(BURST_MEDIA_TONE_LISTEN_STOP,
                                                      burst_burst_media_player_end_callback,
                                                      msg->prority,
                                                      0,
                                                      (void*)msg->seq);
                                isfinished = wl_true;
                            }
                        }
                        break;
                    }
                    default:
                        break;
                }
            }

            // 播放结束,删除
            if (isfinished) {
                WLLOGI("msg %d type %d finished", msg->seq, msg->msgtype);
                if (BURST_MSGTYPE_PTT == msg->msgtype) {
                    burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
                    wl_int32      size   = sizeof(repo_playback) + pttmsg->frameslen;
                    if (WL_MEMORY_SIZE_LIMIT > size) {
                        repo_playback* playback = (repo_playback*)wl_malloc(size);
                        if (playback) {
                            wl_list_iterator  package_iterator;
                            burst_pttpackage* pttpackage = wl_null;
                            wl_uint32         pos        = 0;

                            memset(playback, 0, sizeof(repo_playback));
                            memcpy(&playback->session, &msg->msgsession.session, sizeof(wlsdk_session));
                            playback->senderid = msg->msgsession.senderid;
                            strncpy(playback->sendername, msg->msgsession.sendername, sizeof(playback->sendername));
                            playback->sendername[sizeof(playback->sendername) - 1] = '\0';
                            playback->msgid                                        = msg->msgsession.msgid;
                            playback->created                                      = msg->msgsession.created;

                            pttpackage = (burst_pttpackage*)wl_list_get_first(&pttmsg->package_list, &package_iterator);
                            while (!wl_list_iterator_is_end(&package_iterator)) {
                                if (pos + pttpackage->frameslen <= pttmsg->frameslen) {
                                    memcpy(playback->audio + pos, pttpackage->frames, pttpackage->frameslen);
                                    playback->audiolen += pttpackage->frameslen;
                                    playback->framecount += pttpackage->framecount;
                                    pos += pttpackage->frameslen;
                                }
                                pttpackage = (burst_pttpackage*)wl_list_get_next(&package_iterator);
                            }
                            playback->duration = (playback->framecount + 49) / 50;
                            repo_add_burst_playback(playback);
                            wl_free(playback);
                        }
                    }
                    wl_list_free(&pttmsg->package_list);
                    // PTT全语音STOP提示音在burst_media_set_player_frame_end时插入
                    if (!burst_media_is_playing() && 2 != pttmsg->marker && 3 != pttmsg->marker) {
                        WLLOGD("burst_media_play_tone BURST_MEDIA_TONE_LISTEN_STOP");
                        burst_media_play_tone(BURST_MEDIA_TONE_LISTEN_STOP,
                                              burst_burst_media_player_end_callback,
                                              msg->prority,
                                              0,
                                              (void*)msg->seq);
                    }
                } else if (BURST_MSGTYPE_AUDIO == msg->msgtype) {
                    burst_audiomsg* audiomsg = (burst_audiomsg*)msg->content;
                    wl_int32        size     = sizeof(repo_playback) + audiomsg->audiolen;
                    if (WL_MEMORY_SIZE_LIMIT > size) {
                        repo_playback* playback = (repo_playback*)wl_malloc(size);
                        if (playback) {
                            memcpy(&playback->session, &msg->msgsession.session, sizeof(wlsdk_session));
                            playback->senderid = msg->msgsession.senderid;
                            strncpy(playback->sendername, msg->msgsession.sendername, sizeof(playback->sendername));
                            playback->sendername[sizeof(playback->sendername) - 1] = '\0';
                            playback->msgid                                        = msg->msgsession.msgid;
                            playback->created                                      = msg->msgsession.created;
                            playback->framecount                                   = audiomsg->framecount;
                            playback->duration                                     = (playback->framecount + 49) / 50;
                            playback->audiolen                                     = audiomsg->audiolen;
                            memcpy(playback->audio, audiomsg->audio, audiomsg->audiolen);
                            repo_add_burst_playback(playback);
                            wl_free(playback);
                        }
                    }
                }
                burst_idle(&msg->msgsession.session, end_result->resultcode);
                WLLOGI("wl_list_iterator_remove seq %d", msg->seq);
                burst_message_free(msg);
                wl_list_iterator_remove(&iterator);
            }
            break;
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }
}

static void process_burst_set_burst_session(void* data, wl_int32 datalen) {
    burst_sessioninfo* sessioninfo = (burst_sessioninfo*)data;
    repo_session       session;
    if (wl_null == data || sizeof(burst_sessioninfo) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        burst_sdk_message_callback(WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_LOCAL_DATA_INVALID, wl_null, 0);
        return;
    }
    // 服务会话,检查id是否为服务号id
    if (WLSDK_SESSION_TYPE_SERVICE == sessioninfo->sessiontype) {
        // 会话id为服务号id
        if ((sessioninfo->sessionid & 0xffffffff) != repo_get_userid()) {
            sessioninfo->sessionid = sessioninfo->sessionid << 32 | repo_get_userid();
        }
    }
    // 切换会话先是否旧会话
    if (sessioninfo->sessionid != s_burst_global.active_sessionid
        || sessioninfo->sessiontype != s_burst_global.active_sessiontype) {
        repo_close_session(s_burst_global.active_sessionid, s_burst_global.active_sessiontype);
        burst_release_session(s_burst_global.active_sessionid, s_burst_global.active_sessiontype);
    }
    // 会话不存在
    if (0 != repo_get_session(sessioninfo->sessionid, sessioninfo->sessiontype, &session)) {
        memset(&session, 0, sizeof(repo_session));
        // 检查群是否存在
        if (WLSDK_SESSION_TYPE_GROUP == sessioninfo->sessiontype) {
            repo_group group;
            // 群不存在
            if (0 != repo_get_group(sessioninfo->sessionid, &group)) {
                burst_sdk_message_callback(
                    WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_GROUP_NOT_EXIST, wl_null, 0);
                return;
            } else {
                session.session.sessionid   = group.group.groupid;
                session.session.sessiontype = WLSDK_SESSION_TYPE_GROUP;
                strncpy(session.session.name, group.group.group_name, sizeof(session.session.name));
                session.session.name[sizeof(session.session.name) - 1] = '\0';
                session.session.online_count                           = 1;
                session.latest_active                                  = repo_get_server_timestamp();
                repo_add_session(&session, -1);
            }
        }
        // 个人会话检查好友是否存在
        else if (WLSDK_SESSION_TYPE_SINGLE == sessioninfo->sessiontype) {
            repo_friend friend;
            if (0 != repo_get_friend(sessioninfo->sessionid, &friend)) {
                burst_sdk_message_callback(
                    WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_FRIEND_NOT_FRIEND, wl_null, 0);
                return;
            }
            session.session.sessionid   = friend.friend.userid;
            session.session.sessiontype = WLSDK_SESSION_TYPE_SINGLE;
            strncpy(session.session.name, friend.friend.nick, sizeof(session.session.name));
            session.session.name[sizeof(session.session.name) - 1] = '\0';
            session.session.online_count                           = 1;
            session.latest_active                                  = repo_get_server_timestamp();
            repo_add_session(&session, -1);
        }
        // 非法
        else {
            WLLOGW("session type %d not support", sessioninfo->sessiontype);
            burst_sdk_message_callback(
                WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_LOCAL_DATA_INVALID, wl_null, 0);
            return;
        }
    }
    burst_sdk_message_callback(WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_SUCCESS, wl_null, 0);
    WLLOGI("session %llu type %d", session.session.sessionid, session.session.sessiontype);
    repo_set_default_session(session.session.sessionid, session.session.sessiontype);
    burst_set_active_session(&session);
    s_burst_global.active_lifeleft = 5000;  // 会话切换5秒保护期
}

static void burst_release_session(wl_uint64 sessionid, wl_uint32 sessiontype) {
    // 释放当前会话
    if (sessionid == s_burst_global.active_sessionid && sessiontype == s_burst_global.active_sessiontype) {
        // 讲话中
        if (s_burst_global.burst_pressed) {
            burst_stop_recorder(WLSDK_RESULT_SUCCESS);
        }
        s_burst_global.active_sessionid   = 0;
        s_burst_global.active_sessiontype = 0;
        s_burst_global.active_lifeleft    = 0;
    }
    if (burst_media_is_playing()) {
        burst_msg* msg = burst_get_message((wl_uint32)burst_media_get_player_context());
        if (msg) {
            if (msg->msgsession.session.sessionid == sessionid && msg->msgsession.session.sessiontype == sessiontype) {
                burst_media_stop_player();
                burst_idle(&msg->msgsession.session, WLSDK_RESULT_SUCCESS);
            }
        }
    }
    // 删除会话消息
    burst_delete_session_message(sessionid, sessiontype);
}

static void process_burst_switch(void) {
    wl_time  current_time  = repo_get_server_timestamp();
    wl_list* session_list  = repo_get_session_list();
    wl_int32 session_count = wl_list_size(session_list);
    if (0 == session_count) {
        WLLOGW("session empty");
        burst_sdk_message_callback(
            WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_LOCAL_SESSION_SWITCH_EMPTY, wl_null, 0);
        return;
    } else if (1 == session_count) {
        WLLOGW("session is only one");
        burst_sdk_message_callback(
            WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_LOCAL_SESSION_SWITCH_EMPTY, wl_null, 0);
        return;
    } else {
        repo_session session;
        // 切会话序号重置
        if (current_time > s_burst_global.burst_switch_timestamp + BURST_SWITCH_IDX_RESET_INTERVAL) {
            s_burst_global.burst_switch_idx = 1;
        } else {
            // 一轮没切换完,idx自增
            if ((s_burst_global.burst_switch_idx + 1) < session_count) {
                s_burst_global.burst_switch_idx++;
            }
            // 一轮切换完成,每次切换最末尾那个会话
            else {
                s_burst_global.burst_switch_idx = session_count - 1;
            }
        }
        s_burst_global.burst_switch_timestamp = current_time;
        if (0 == repo_get_session_by_pos(s_burst_global.burst_switch_idx, &session)) {
            burst_set_active_session(&session);
            s_burst_global.active_lifeleft = 5000;  // 会话切换5秒保护期
        } else {
            burst_sdk_message_callback(
                WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_LOCAL_DATA_INVALID, wl_null, 0);
        }
        return;
    }
}

static void burst_stop_player_immediately(void) {
    if (burst_media_is_playing()) {
        burst_msg* msg = burst_get_message((wl_uint32)burst_media_get_player_context());
        if (msg) {
            switch (msg->msgtype) {
                case BURST_MSGTYPE_AUDIO:
                case BURST_MSGTYPE_PLAYBACK:
                case BURST_MSGTYPE_PTT:
                    burst_idle(&msg->msgsession.session, WLSDK_RESULT_SUCCESS);
                    break;
                default:
                    break;
            }
        }
        burst_media_stop_player_immediately();
    }
}

static void burst_stop_player(void) {
    if (burst_media_is_playing()) {
        burst_msg* msg = burst_get_message((wl_uint32)burst_media_get_player_context());
        if (msg) {
            switch (msg->msgtype) {
                case BURST_MSGTYPE_AUDIO:
                case BURST_MSGTYPE_PLAYBACK:
                case BURST_MSGTYPE_PTT:
                    burst_idle(&msg->msgsession.session, WLSDK_RESULT_SUCCESS);
                    break;
                default:
                    break;
            }
        }
        burst_media_stop_player();
    }
}

static void burst_stop_recorder(wl_int32 result) {
    if (burst_media_is_recording()) {
        burst_media_stop_recorder();
        burst_idle(&s_burst_global.burst_request.session, result);
    }
    // 按键释放一定在stop recorder之后,因为stop record要发送录音尾音
    s_burst_global.burst_pressed = wl_false;
}

static void process_burst_remove_session(void* data, wl_int32 datalen) {
    repo_session       session;
    burst_sessioninfo* sessioninfo = (burst_sessioninfo*)data;
    if (wl_null == data || sizeof(burst_sessioninfo) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        burst_sdk_message_callback(WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_LOCAL_DATA_INVALID, wl_null, 0);
        return;
    }
    burst_release_session(sessioninfo->sessionid, sessioninfo->sessiontype);
    // 删除会话
    repo_del_session(sessioninfo->sessionid, sessioninfo->sessiontype);
    burst_sdk_message_callback(WLSDK_MSG_SESSION_REMOVE_SESSION, WLSDK_RESULT_SUCCESS, wl_null, 0);
    // 更新会话列表
    wl_core_send_message(WL_MODULE_SESSION, SESSION_MSG_GET_SESSIONS, wl_null, 0);
    memset(&session, 0, sizeof(session));
    burst_check_active_session(&session);
}

static void process_burst_close_session(void* data, wl_int32 datalen) {
    repo_session       session;
    burst_sessioninfo* sessioninfo = (burst_sessioninfo*)data;
    if (wl_null == data || sizeof(burst_sessioninfo) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        burst_sdk_message_callback(WLSDK_MSG_SESSION_SET_BURST_SESSION, WLSDK_RESULT_LOCAL_DATA_INVALID, wl_null, 0);
        return;
    }
    repo_close_session(sessioninfo->sessionid, sessioninfo->sessiontype);
    burst_release_session(sessioninfo->sessionid, sessioninfo->sessiontype);
    burst_sdk_message_callback(WLSDK_MSG_SESSION_CLOSE_SESSION, WLSDK_RESULT_SUCCESS, wl_null, 0);
    memset(&session, 0, sizeof(session));
    burst_check_active_session(&session);
}

static void process_burst_msgdata_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGI("burst msg result %d", message->resultCode);
    // 发送失败
    if (WLSDK_RESULT_SUCCESS != message->resultCode) {
        if (content) {
            wlsdk_session* session = (wlsdk_session*)content;
            if (session->sessionid == s_burst_global.burst_request.session.sessionid
                && session->sessiontype == s_burst_global.burst_request.session.sessiontype
                && s_burst_global.burst_pressed) {
                // 话权请求失败释放按键要在stop recorder之前 不在发送录音
                s_burst_global.burst_pressed = wl_false;
                burst_media_stop_recorder();
                burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                                      burst_burst_media_player_end_callback,
                                      BURST_MEDIA_PRORITY_ALARM,
                                      0,
                                      (void*)wl_null);
                burst_idle(session, message->resultCode);
                memset(&s_burst_global.burst_request, 0, sizeof(burst_request));
            }
            // 用户不是群成员
            if (WLSDK_SESSION_TYPE_GROUP == session->sessiontype
                && WLSDK_RESULT_GROUP_USER_NOT_MEMBER == message->resultCode) {
                session_sessioninfo msgdata;
                msgdata.sessionid   = session->sessionid;
                msgdata.sessiontype = session->sessiontype;
                wl_core_send_message(WL_MODULE_BURST, BURST_MSG_REMOVE_SESSION, &msgdata, sizeof(msgdata));
            }
        }
        // 语音超时,检查心跳
        if (WLSDK_RESULT_LOCAL_REQUEST_TIMEOUT == message->resultCode) {
            WLLOGW("msg timeout");
            wl_core_send_message(WL_MODULE_KEEPALIVE, KEEPALIVE_MSG_SEND_HEARTBEAT, wl_null, 0);
        }
    } else {
        PBSendMsgRsp* response = (PBSendMsgRsp*)message->pContent;
        if (content) {
            wlsdk_session* session = (wlsdk_session*)content;
            repo_session   sessioninfo;
            // 大于0才有效(在线成员更新)
            if (response->onlineCount) {
                repo_update_session_online(session->sessionid, session->sessiontype, response->onlineCount);
            }
            // 当前会话为单人会话
            if (session->sessionid == s_burst_global.burst_request.session.sessionid
                && session->sessiontype == s_burst_global.burst_request.session.sessiontype
                && WLSDK_SESSION_TYPE_SINGLE == session->sessiontype) {
                // 对方用户不在线
                if (response->remoteOffline) {
                    s_burst_global.burst_pressed = wl_false;
                    burst_media_stop_recorder();
                    // 支持TTS平台不播放
#ifndef TTS_SUPPORT
                    burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                                          burst_burst_media_player_end_callback,
                                          BURST_MEDIA_PRORITY_ALARM,
                                          0,
                                          (void*)wl_null);
#endif
                    burst_idle(session, WLSDK_RESULT_LOCAL_SESSION_REMOTE_OFFLINE);
                    memset(&s_burst_global.burst_request, 0, sizeof(burst_request));
                }
            }
        }
    }
}

static void process_burst_control_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    if (WLSDK_RESULT_SUCCESS != message->resultCode) {
        if (content) {
            // PTT 按下状态 请权响应
            if (s_burst_global.burst_pressed) {
                wlsdk_session* session = (wlsdk_session*)content;
                if (session->sessionid == s_burst_global.burst_request.session.sessionid
                    && session->sessiontype == s_burst_global.burst_request.session.sessiontype
                    && message->seqNum == s_burst_global.burst_request.seq && s_burst_global.burst_pressed) {
                    // 话权请求失败释放按键要在stop recorder之前 不在发送录音
                    s_burst_global.burst_pressed = wl_false;
                    burst_media_stop_recorder();
                    burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                                          burst_burst_media_player_end_callback,
                                          BURST_MEDIA_PRORITY_ALARM,
                                          0,
                                          (void*)wl_null);
                    burst_idle(session, message->resultCode);
                    memset(&s_burst_global.burst_request, 0, sizeof(burst_request));
                }
            }
        }
    }
}

static void process_burst_msgdata_notify_message(PBWeilaMsg* message, void* context) {
    PBMsgData* notify = (PBMsgData*)message->pContent;
    if (IM_VOIP_DATA_TYPE == notify->msgType) {
        process_voip_notify_message(notify->senderId, notify->content, notify->contentSize);
    } else {
        // 本人其他设备消息过滤
        repo_user        userinfo;
        repo_session     session;
        burst_msgsession msgsession;

        repo_get_selfinfo(&userinfo);
        // 本人消息过滤
        if (userinfo.user.userid == notify->senderId) {
            WLLOGI("self message");
            return;
        }
        // 个人会话sessionId为senderId
        if (REPO_SESSION_TYPE_SINGLE == notify->sessionType || REPO_SESSION_TYPE_CORP_SINGLE == notify->sessionType) {
            notify->sessionId   = notify->senderId;
            notify->sessionType = WLSDK_SESSION_TYPE_SINGLE;  // 个人会话类型统一
        }
        // 群会话类型统一
        if (REPO_SESSION_TYPE_CORP_GROUP == notify->sessionType
            || REPO_SESSION_TYPE_CORP_GROUP_TMP == notify->sessionType) {
            notify->sessionType = WLSDK_SESSION_TYPE_GROUP;
        }
#if 0
        // msg_list 占用最大内存数统计
        wl_uint32 count = burst_cached_count();
        //缓存只比临界多32K内存,只接收单呼
        if (count >= (WL_MEMORY_PTT_CACHE_MAX - 32 * 1024)) {
            if (WLSDK_SESSION_TYPE_SINGLE != notify->sessionType) {
                WLLOGW("cached memory %d too large only recv single and active session", count);
                return;
            }
        }
        //缓存大小超过PTT缓存限制,不再接收消息
        if (count >= WL_MEMORY_PTT_CACHE_MAX) {
            WLLOGW("cached memory %d too large", count);
            return;
        }
#else
        // 只处理优先级最高的消息
        // wl_int32 activesession_prority =
        //     repo_get_session_prority(s_burst_global.active_sessionid, s_burst_global.active_sessiontype);
        // wl_int32 newsession_prority = repo_get_session_prority(notify->sessionId, notify->sessionType);
        // if (newsession_prority < activesession_prority) {
        //     WLLOGW("session id %lld type %d prority low than active session id %lld type %d",
        //            notify->sessionId,
        //            notify->sessionType,
        //            s_burst_global.active_sessionid,
        //            s_burst_global.active_sessiontype);
        //     return;
        // }
        // 个人会话临界
        if (WL_MEMORY_PTT_SINGLE_IS_THRESHOLD) {
            // 文本短消息不过滤
            if (notify->msgType != IM_TEXT_MSG_TYPE && notify->contentSize < MAX_TTS_TEXT_LEN / 2) {
                if (WLSDK_SESSION_TYPE_SINGLE != notify->sessionType) {
                    WLLOGW("cached memory too large only recv single and active session fresh size %d",
                           wl_mem_fresh_size());
                    return;
                }
            }
        }
#endif
        WLLOGD("session msg %d mtype %d id %lld type %d sender id %d name %s",
               notify->msgId,
               notify->msgType,
               notify->sessionId,
               notify->sessionType,
               notify->senderId,
               notify->senderName);
        // 新会话
        if (0 != repo_get_session(notify->sessionId, notify->sessionType, &session)) {
            memset(&session, 0, sizeof(repo_session));
            session.session.sessionid    = notify->sessionId;
            session.session.sessiontype  = notify->sessionType;
            session.session.online_count = 1;
            if (repo_is_normal_group_session(notify->sessionId, notify->sessionType)) {
                session.latest_burst = 0;
            } else {
                session.latest_burst = repo_get_server_timestamp();
            }
            session.sync_timestamp = 0;
            // 个人会话
            if (WLSDK_SESSION_TYPE_SINGLE == notify->sessionType) {
                repo_friend friend;
                if (0 == repo_get_friend(notify->sessionId, &friend)) {
                    strncpy(session.session.name, friend.friend.nick, sizeof(session.session.name));
                    session.session.name[sizeof(session.session.name) - 1] = '\0';
                } else {
                    strncpy(session.session.name, notify->senderName, sizeof(session.session.name));
                    session.session.name[sizeof(session.session.name) - 1] = '\0';
                    session.sync_timestamp                                 = 1;
                }
            }
            // 群会话
            else if (WLSDK_SESSION_TYPE_GROUP == notify->sessionType) {
                repo_group group;
                if (0 == repo_get_group(notify->sessionId, &group)) {
                    strncpy(session.session.name, group.group.group_name, sizeof(session.session.name));
                    session.session.name[sizeof(session.session.name) - 1] = '\0';
                } else {
                    strncpy(session.session.name, BURST_GROUP_TMP_NAME, sizeof(session.session.name));
                    session.session.name[sizeof(session.session.name) - 1] = '\0';
                    session.sync_timestamp                                 = 1;
                }
            }
            // 服务会话
            else if (WLSDK_SESSION_TYPE_SERVICE == notify->sessionType) {
                strncpy(session.session.name, BURST_SERVICE_NAME, sizeof(session.session.name));
                session.session.name[sizeof(session.session.name) - 1] = '\0';
            } else {
                WLLOGD("session id %lld type %d not support", notify->sessionId, notify->sessionType);
                return;
            }
            session.session.online_count = notify->onlineCount;
            repo_add_session(&session, -1);
        } else {
            session.session.online_count = notify->onlineCount;
        }
        memset(&msgsession, 0, sizeof(burst_msgsession));
        memcpy(&msgsession.session, &session.session, sizeof(wlsdk_session));
        msgsession.senderid = notify->senderId;
        strncpy(msgsession.sendername, notify->senderName, sizeof(msgsession.sendername));
        msgsession.sendername[sizeof(msgsession.sendername) - 1] = '\0';
        msgsession.msgid                                         = notify->senderId;
        msgsession.created                                       = notify->created;
        switch (notify->msgType) {
            case IM_TEXT_MSG_TYPE:
                process_text_notify_message(&msgsession, notify->content, notify->contentSize);
                break;
            case IM_PTT_DATA_TYPE:
                process_ptt_notify_message(&msgsession, notify->content, notify->contentSize);
                break;
            default:
                break;
        }
    }
}

static void process_burst_control_notify_message(PBWeilaMsg* message, void* context) {
    PBBurstCtlNtf* notify = (PBBurstCtlNtf*)message->pContent;
    repo_session   session;
    // 个人会话类型统一
    if (REPO_SESSION_TYPE_SINGLE == notify->sessionType || REPO_SESSION_TYPE_CORP_SINGLE == notify->sessionType) {
        notify->sessionType = WLSDK_SESSION_TYPE_SINGLE;
    }
    // 群会话类型统一
    if (REPO_SESSION_TYPE_CORP_GROUP == notify->sessionType
        || REPO_SESSION_TYPE_CORP_GROUP_TMP == notify->sessionType) {
        notify->sessionType = WLSDK_SESSION_TYPE_GROUP;
    }
    if (0 != repo_get_session(notify->sessionId, notify->sessionType, &session)) {
        WLLOGW("session id %lld type %d not exist", notify->sessionId, notify->sessionType);
        return;
    }

    switch (notify->burstType) {
        // 释放talker话权
        case BURST_CTRL_INTERRUPT:
        case BURST_CTRL_REVOKE: {
            if (s_burst_global.burst_pressed) {
                if (session.session.sessionid == s_burst_global.burst_request.session.sessionid
                    && session.session.sessiontype == s_burst_global.burst_request.session.sessiontype) {
                    // 话权请求失败释放按键要在stop recorder之前 不在发送录音
                    s_burst_global.burst_pressed = wl_false;
                    burst_media_stop_recorder();
                    burst_media_play_tone(BURST_MEDIA_TONE_DENY,
                                          burst_burst_media_player_end_callback,
                                          BURST_MEDIA_PRORITY_ALARM,
                                          0,
                                          (void*)wl_null);
                    if (BURST_CTRL_INTERRUPT == notify->burstType) {
                        burst_idle(&session.session, WLSDK_RESULT_LOCAL_BURST_INTERRUPT);
                    } else {
                        burst_idle(&session.session, WLSDK_RESULT_LOCAL_BURST_REVOKE);
                    }
                    memset(&s_burst_global.burst_request, 0, sizeof(burst_request));
                }
            }
            break;
        }
        default:
            break;
    }
}

static void process_monitor_control_notify_message(PBWeilaMsg* message, void* context) {
    PBMonitorCtlNtf* notify = (PBMonitorCtlNtf*)message->pContent;

    // 关闭监听
    if (0 == notify->status) {
        burst_monitor_stop(notify->monitorId);
    } else {
        burst_monitor_start(notify->monitorId, notify->duration);
    }
}

static void process_text_notify_message(burst_msgsession* msgsession, void* content, wl_uint32 contentlen) {
    burst_msg*     msg     = wl_null;
    burst_textmsg* textmsg = wl_null;
    if (contentlen >= MAX_TTS_TEXT_LEN) {
        WLLOGW("content %d too long", contentlen);
        return;
    }

    msg = (burst_msg*)wl_malloc(sizeof(burst_msg) + sizeof(burst_textmsg) + contentlen + 1);
    if (wl_null == msg) {
        WLLOGW("malloc fail");
        return;
    }
    memset(msg, 0, sizeof(burst_msg) + sizeof(burst_textmsg) + contentlen + 1);
    msg->seq = ++s_burst_global.msg_seq;
    memcpy(&msg->msgsession, msgsession, sizeof(burst_msgsession));
    msg->msgtype    = BURST_MSGTYPE_TEXT;
    msg->prority    = BURST_MEDIA_PRORITY_TEXT;
    msg->lifetime   = BURST_MSG_LIFETIME_MAX;
    msg->alloccount = sizeof(burst_msg) + sizeof(burst_textmsg) + contentlen + 1;
    msg->contentlen = sizeof(burst_textmsg) + contentlen + 1;
    textmsg         = (burst_textmsg*)msg->content;
    strncpy(textmsg->text, content, contentlen);
    textmsg->text[contentlen] = '\0';
    textmsg->textlen          = contentlen;
    if (-1 == wl_list_add(&s_burst_global.msg_list, msg, -1)) {
        burst_message_free(msg);
        WLLOGW("list add fail");
    }
}

static void process_ptt_notify_message(burst_msgsession* msgsession, void* content, wl_uint32 contentlen) {
    PBPtt* ptt = (PBPtt*)content;
    // PTT帧是否存在
    wl_list_iterator iterator;
    burst_msg*       msg = (burst_msg*)wl_list_get_first(&s_burst_global.msg_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // PTT消息
        if (BURST_MSGTYPE_PTT == msg->msgtype) {
            burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
            // 消息匹配
            if (msg->msgsession.session.sessionid == msgsession->session.sessionid
                && msg->msgsession.session.sessiontype == msgsession->session.sessiontype
                && msg->msgsession.senderid == msgsession->senderid && msg->msgsession.msgid == msgsession->msgid
                && pttmsg->seq == ptt->seq) {
                pttmsg->marker = ptt->marker;
                break;
            }
        }
        msg = (burst_msg*)wl_list_get_next(&iterator);
    }

    // 新消息
    if (wl_null == msg) {
        burst_pttmsg* pttmsg = wl_null;
        msg                  = (burst_msg*)wl_malloc(sizeof(burst_msg) + sizeof(burst_pttmsg));
        if (wl_null == msg) {
            WLLOGW("malloc fail");
            return;
        }
        memset(msg, 0, sizeof(burst_msg) + sizeof(burst_pttmsg));
        msg->seq = ++s_burst_global.msg_seq;
        memcpy(&msg->msgsession, msgsession, sizeof(burst_msgsession));
        msg->msgtype    = BURST_MSGTYPE_PTT;
        msg->prority    = BURST_MEDIA_PRORITY_PTT;
        msg->alloccount = sizeof(burst_msg) + sizeof(burst_pttmsg);
        msg->contentlen = sizeof(burst_pttmsg);
        msg->lifetime   = BURST_MSG_LIFETIME_MAX;
        pttmsg          = (burst_pttmsg*)msg->content;
        // pttmsg->marker     = 1;
        pttmsg->marker     = ptt->marker;
        pttmsg->seq        = ptt->seq;
        pttmsg->packageseq = ptt->packageSeq;
        pttmsg->source     = ptt->source;

        if (-1 == wl_list_add(&s_burst_global.msg_list, msg, -1)) {
            burst_message_free(msg);
            WLLOGW("list add fail");
            return;
        }
    }
    msg->lifetime = BURST_MSG_LIFETIME_MAX;
    // 计算ptt frame需要内存长度
    if (ptt->frameList.listCount) {
        wl_uint32         datalen    = 0;
        burst_pttpackage* pttpackage = wl_null;
        PBLinkItem*       item       = ptt->frameList.pLinks;
        while (item) {
            // 每帧2字节记录帧长
            datalen += sizeof(wl_uint16) + item->dataSize;
            item = item->pNext;
        }
        // 添加PTT Package
        pttpackage = (burst_pttpackage*)wl_malloc(sizeof(burst_pttpackage) + datalen);
        if (pttpackage) {
            wl_uint32     pos      = 0;
            wl_uint16     framelen = 0;
            burst_pttmsg* pttmsg   = wl_null;
            memset(pttpackage, 0, sizeof(burst_pttpackage));
            pttpackage->framecount = ptt->frameList.listCount;
            pttpackage->frameslen  = datalen;
            item                   = ptt->frameList.pLinks;
            while (item) {
                framelen = (wl_uint16)item->dataSize;
                memcpy(pttpackage->frames + pos, &framelen, sizeof(wl_uint16));
                pos += sizeof(wl_uint16);
                memcpy(pttpackage->frames + pos, item->data, framelen);
                pos += framelen;
                item = item->pNext;
            }
            pttmsg = (burst_pttmsg*)msg->content;
            if (-1 == wl_list_add(&pttmsg->package_list, pttpackage, -1)) {
                wl_free(pttpackage);
                WLLOGW("list add fail");
                return;
            }
            pttmsg->framecount += pttpackage->framecount;
            pttmsg->frameslen += pttpackage->frameslen;
            WLLOGI("framecount is %d isplaying %d seq %d context %d marker %d",
                   pttmsg->framecount,
                   burst_media_is_playing(),
                   msg->seq,
                   burst_media_get_player_context(),
                   pttmsg->marker);
            // 当前PTT播放中
            if (burst_media_is_playing() && (wl_uint32)burst_media_get_player_context() == msg->seq) {
                burst_media_add_play_audio(pttpackage->frames, pttpackage->frameslen, pttpackage->framecount);
                // 消息生命期统一为BURST_MSG_LIFETIME_MAX方便统一管理回收
                //  msg->lifetime += pttpackage->framecount * BURST_FRAME_TIME;
                //  PTT结束帧
                if (2 == pttmsg->marker || 3 == pttmsg->marker) {
                    burst_media_play_tone(BURST_MEDIA_TONE_LISTEN_STOP,
                                          burst_burst_media_player_end_callback,
                                          msg->prority,
                                          0,
                                          (void*)msg->seq);
                }
            }
            msg->alloccount += pttpackage->frameslen;
        }
    } else {
        burst_pttmsg* pttmsg = (burst_pttmsg*)msg->content;
        WLLOGI("framecount is 0 isplaying %d seq %d context %d marker %d",
               burst_media_is_playing(),
               msg->seq,
               burst_media_get_player_context(),
               pttmsg->marker);
        // 当前PTT播放中
        if (burst_media_is_playing() && (wl_uint32)burst_media_get_player_context() == msg->seq) {
            // PTT结束帧
            if (2 == pttmsg->marker || 3 == pttmsg->marker) {
                burst_media_play_tone(BURST_MEDIA_TONE_LISTEN_STOP,
                                      burst_burst_media_player_end_callback,
                                      msg->prority,
                                      0,
                                      (void*)msg->seq);
            }
        }
    }
}

static void burst_voip_send_frames(PBLinkList* framelist) {
    PBBurstData   burstData;
    repo_user     user;
    repo_hardware hardware;
    PBEncResult   enc_result;
    wlsdk_session session;
    wl_int32      resultcode = WLSDK_RESULT_FAILURE;

    repo_get_selfinfo(&user);
    repo_get_hardware(&hardware);
    memset(&burstData, 0, sizeof(PBBurstData));

    burstData.sessionId = s_burst_global.burst_voip.session_voip.userid;
    burstData.sessionType =
        repo_get_session_message_session_type(s_burst_global.burst_voip.session_voip.userid, WLSDK_SESSION_TYPE_SINGLE);
    burstData.senderId   = user.user.userid;
    burstData.sourceType = hardware.source_type;
    burstData.burstSeq   = s_burst_global.burst_voip.seq;
    burstData.packetSeq  = 0;
    burstData.marker     = 1;
    burstData.isVoip     = wl_true;
    burstData.pFrameList = framelist;
    enc_result           = weilapb_buildSendBurstData(&burstData);
    session.sessionid    = s_burst_global.burst_voip.session_voip.userid;
    session.sessiontype  = WLSDK_SESSION_TYPE_SINGLE;
    resultcode           = protocol_client_send_request(&enc_result,
                                              &session,
                                              sizeof(wlsdk_session),
                                              PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                              burst_protocol_client_voip_request_callback,
                                              (void*)s_burst_global.burst_voip.seq);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
    }
}

static void process_burst_voip_invite(void* data, wl_int32 datalen) {
    wl_uint32* userId     = (wl_uint32*)data;
    wl_int32   resultcode = WL_RESULT_SUCCESS;
    repo_friend friend;
    PBEncResult enc_result;

    s_burst_global.burst_voip.seq                   = (wl_int32)repo_get_server_timestamp();
    s_burst_global.burst_voip.lifeleft              = BURST_VOIP_LIFE_LEFT;
    s_burst_global.burst_voip.session_voip.iscaller = wl_true;
    s_burst_global.burst_voip.session_voip.state    = WLSDK_VOIP_CALLING;
    s_burst_global.burst_voip.session_voip.userid   = *userId;
    if (0 == repo_get_friend(*userId, &friend)) {
        strncpy(s_burst_global.burst_voip.session_voip.username,
                friend.friend.nick,
                sizeof(s_burst_global.burst_voip.session_voip.username) - 1);
    }
    sprintf(s_burst_global.burst_voip.callid, "%d-%d-%d", repo_get_userid(), *userId, s_burst_global.burst_voip.seq);

    enc_result = weilapb_buildVoipInviteReq(s_burst_global.burst_voip.callid, *userId);
    resultcode = protocol_client_send_request(&enc_result,
                                              &s_burst_global.burst_voip,
                                              sizeof(s_burst_global.burst_voip),
                                              PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                              burst_protocol_client_voip_request_callback,
                                              wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
        burst_voip_terminate(resultcode);
        return;
    }
    burst_voip_state();
}

static void process_burst_voip_accept(void* data, wl_int32 datalen) {
    wl_int32 resultcode = WL_RESULT_SUCCESS;

    if (WLSDK_VOIP_RING == s_burst_global.burst_voip.session_voip.state) {
        PBEncResult enc_result =
            weilapb_buildVoipAcceptReq(s_burst_global.burst_voip.callid, s_burst_global.burst_voip.session_voip.userid);
        resultcode = protocol_client_send_request(&enc_result,
                                                  &s_burst_global.burst_voip,
                                                  sizeof(s_burst_global.burst_voip),
                                                  PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                                  burst_protocol_client_voip_request_callback,
                                                  wl_null);
        if (0 > resultcode) {
            WLLOGW("protocol_client_send_request error");
            burst_voip_terminate(resultcode);
            return;
        }
    } else {
        burst_voip_terminate(WLSDK_RESULT_LOCAL_VOIP_HANGUP);
    }
}

static void burst_voip_hangup(void) {
    wl_int32 resultcode = WL_RESULT_SUCCESS;

    PBEncResult enc_result =
        weilapb_buildVoipAcceptReq(s_burst_global.burst_voip.callid, s_burst_global.burst_voip.session_voip.userid);
    resultcode = protocol_client_send_request(&enc_result,
                                              &s_burst_global.burst_voip,
                                              sizeof(s_burst_global.burst_voip),
                                              PROTOCOL_CLIENT_REQUEST_TIMEOUT_15,
                                              burst_protocol_client_voip_request_callback,
                                              wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
    }
    burst_voip_terminate(resultcode);
}

static void process_burst_voip_hangup(void* data, wl_int32 datalen) {
    burst_voip_hangup();
}

static void process_burst_button_press(void* data, wl_int32 datalen) {
    wl_int32*   index = (wl_int32*)data;
    repo_button button;
    memset(&button, 0, sizeof(repo_button));
    if (0 > repo_get_extension_config_button(*index, &button)) {
        WLLOGW("invalid button index %d", *index);
        return;
    }
    // 电话呼叫
    if (1 == button.type) {
        burst_sdk_message_callback(
            WLSDK_MSG_TELEPHONE_CALL, WLSDK_RESULT_SUCCESS, button.number, strlen(button.number));
    } else {
        repo_session session;
        // 切换会话是否旧会话
        if (button.sessionid != s_burst_global.active_sessionid
            || button.sessiontype != s_burst_global.active_sessiontype) {
            repo_close_session(s_burst_global.active_sessionid, s_burst_global.active_sessiontype);
            burst_release_session(s_burst_global.active_sessionid, s_burst_global.active_sessiontype);
        }
        // 会话不存在
        if (0 != repo_get_session(button.sessionid, button.sessiontype, &session)) {
            memset(&session, 0, sizeof(repo_session));
            session.session.sessionid   = button.sessionid;
            session.session.sessiontype = button.sessiontype;
            strncpy(session.session.name, button.name, sizeof(session.session.name));
            session.session.name[sizeof(session.session.name) - 1] = '\0';
            session.session.online_count                           = 1;
            if (repo_is_normal_group_session(button.sessionid, button.sessiontype)) {
                session.latest_burst = 0;
            } else {
                session.latest_burst = repo_get_server_timestamp();
            }
            session.sync_timestamp = 0;
        }
        burst_set_active_session(&session);
        burst_ptt_request();
    }
}

static void process_burst_button_release(void* data, wl_int32 datalen) {
    wl_int32*   index = (wl_int32*)data;
    repo_button button;
    if (0 > repo_get_extension_config_button(*index, &button)) {
        WLLOGW("invalid button index %d", *index);
        return;
    }
    // 电话呼叫只处理按下
    if (1 == button.type) {
        return;
    } else {
        burst_ptt_release();
    }
}

static void process_burst_voip_invite_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    if (WLSDK_RESULT_SUCCESS != message->resultCode) {
        burst_voip_terminate(message->resultCode);
    } else {
        PBVoipInviteRsp* response                    = (PBVoipInviteRsp*)message->pContent;
        s_burst_global.burst_voip.session_voip.state = WLSDK_VOIP_RING;
        if (strlen(s_burst_global.burst_voip.session_voip.username) == 0) {
            if (strlen(response->userInfo.nick) > 0) {
                strncpy(s_burst_global.burst_voip.session_voip.username,
                        response->userInfo.nick,
                        sizeof(s_burst_global.burst_voip.session_voip.username) - 1);
            } else {
                strncpy(s_burst_global.burst_voip.session_voip.username,
                        response->userInfo.number,
                        sizeof(s_burst_global.burst_voip.session_voip.username) - 1);
            }
        }
        burst_voip_ring();
        // 播放振铃
        burst_media_play_tone(BURST_MEDIA_TONE_VOIP_RING,
                              burst_burst_media_player_end_callback,
                              BURST_MEDIA_PRORITY_VOIP_TONE,
                              BURST_VOIP_LIFE_LEFT,
                              wl_null);
    }
}

static void process_burst_voip_accept_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    if (WLSDK_RESULT_SUCCESS != message->resultCode) {
        burst_voip_terminate(message->resultCode);
    } else {
        burst_voip_establish();
    }
}

static void process_voip_invite_notify_message(PBWeilaMsg* message, void* context) {
    PBVoipInviteNtf* notify = (PBVoipInviteNtf*)message->pContent;
    memset(&s_burst_global.burst_voip, 0, sizeof(s_burst_global.burst_voip));
    strncpy(s_burst_global.burst_voip.callid, notify->callId, sizeof(s_burst_global.burst_voip.callid) - 1);
    s_burst_global.burst_voip.seq                   = (wl_int32)repo_get_server_timestamp();
    s_burst_global.burst_voip.lifeleft              = BURST_VOIP_LIFE_LEFT;
    s_burst_global.burst_voip.session_voip.iscaller = wl_false;
    s_burst_global.burst_voip.session_voip.state    = WLSDK_VOIP_RING;
    s_burst_global.burst_voip.session_voip.userid   = notify->userInfo.userId;
    if (strlen(s_burst_global.burst_voip.session_voip.username) == 0) {
        if (strlen(notify->userInfo.nick) > 0) {
            strncpy(s_burst_global.burst_voip.session_voip.username,
                    notify->userInfo.nick,
                    sizeof(s_burst_global.burst_voip.session_voip.username) - 1);
        } else {
            strncpy(s_burst_global.burst_voip.session_voip.username,
                    notify->userInfo.number,
                    sizeof(s_burst_global.burst_voip.session_voip.username) - 1);
        }
    }
    burst_voip_incoming();
}

static void process_voip_accept_notify_message(PBWeilaMsg* message, void* context) {
    PBVoipInviteNtf* notify = (PBVoipInviteNtf*)message->pContent;
    strncpy(s_burst_global.burst_voip.callid, notify->callId, sizeof(s_burst_global.burst_voip.callid) - 1);
    s_burst_global.burst_voip.session_voip.userid = notify->userId;
    if (strlen(s_burst_global.burst_voip.session_voip.username) == 0) {
        if (strlen(notify->userInfo.nick) > 0) {
            strncpy(s_burst_global.burst_voip.session_voip.username,
                    notify->userInfo.nick,
                    sizeof(s_burst_global.burst_voip.session_voip.username) - 1);
        } else {
            strncpy(s_burst_global.burst_voip.session_voip.username,
                    notify->userInfo.number,
                    sizeof(s_burst_global.burst_voip.session_voip.username) - 1);
        }
    }
    burst_voip_establish();
}

static void process_voip_hangup_notify_message(PBWeilaMsg* message, void* context) {
    if (WLSDK_VOIP_NONE != s_burst_global.burst_voip.session_voip.state) {
        burst_voip_terminate(WLSDK_RESULT_SUCCESS);
    }
}

static void process_burst_voip_record_data(void* data, wl_int32 datalen) {
    if (WLSDK_VOIP_ESTABLISH == s_burst_global.burst_voip.session_voip.state) {
        burst_record_data* record_data = (burst_record_data*)data;
        PBLinkList         framelist;
        wl_uint32          pos = 0;
        wl_uint32          idx = 0;

        memset(&framelist, 0, sizeof(framelist));
        for (idx = 0; idx < record_data->framecount; idx++) {
            wl_uint16 framelen = 0;
            wl_char*  data     = wl_null;
            memcpy(&framelen, record_data->frames + pos, sizeof(wl_uint16));
            pos += sizeof(wl_uint16);
            data = record_data->frames + pos;
            burst_pblink_add_tail(&framelist, data, framelen);
            pos += framelen;
            s_burst_global.burst_voip.framecount++;
        }
        WLLOGI("data len %d listCount %d", datalen, framelist.listCount);
        s_burst_global.burst_voip.session_voip.duration = (s_burst_global.burst_voip.framecount + 49) / 50;
        burst_voip_send_frames(&framelist);
        burst_pblink_free(&framelist);
    }
}

static void process_burst_voip_record_end(void* data, wl_int32 datalen) {
    WLLOGI("process_burst_voip_record_end");
    burst_media_stop_voip();
}

static void process_burst_voip_play_result(void* data, wl_int32 datalen) {
    WLLOGI("process_burst_voip_play_result");
}

static void process_burst_voip_play_end(void* data, wl_int32 datalen) {
    WLLOGI("process_burst_voip_play_end");
}

static void process_burst_set_voip_auto_accept(void* data, wl_int32 datalen) {
    burst_voip_auto_accept* voip_auto_accept = (burst_voip_auto_accept*)data;
    s_burst_global.voip_auto_accept          = voip_auto_accept->auto_accept;
    if (voip_auto_accept->delay_ms >= BURST_VOIP_LIFE_LEFT) {
        s_burst_global.voip_delay_ms = BURST_VOIP_LIFE_LEFT;
    } else {
        s_burst_global.voip_delay_ms = voip_auto_accept->delay_ms;
    }
}

static void process_voip_notify_message(wl_uint32 userId, void* content, wl_uint32 contentlen) {
    PBPtt* ptt = (PBPtt*)content;
    // 是否当前voip消息
    if (userId == s_burst_global.burst_voip.session_voip.userid) {
        if (WLSDK_VOIP_ESTABLISH == s_burst_global.burst_voip.session_voip.state) {
            wl_uint32 datalen    = 0;
            wl_uint32 framecount = 0;

            PBLinkItem* item = ptt->frameList.pLinks;
            while (item) {
                // 每帧2字节记录帧长
                datalen += sizeof(wl_uint16) + item->dataSize;
                framecount++;
                item = item->pNext;
            }
            s_burst_global.burst_voip.lifeleft = BURST_VOIP_LIFE_LEFT;
            if (burst_media_is_playing()) {
                // 语音堆积超过1秒丢弃
                if (burst_media_get_player_left_frames() <= 100) {
                    if (datalen) {
                        wl_char* frame = (wl_char*)wl_malloc(datalen);
                        if (frame) {
                            wl_uint16 framelen = 0;
                            wl_uint32 pos      = 0;
                            item               = ptt->frameList.pLinks;
                            while (item) {
                                framelen = (wl_uint16)item->dataSize;
                                memcpy(frame + pos, &framelen, sizeof(wl_uint16));
                                pos += sizeof(wl_uint16);
                                memcpy(frame + pos, item->data, framelen);
                                pos += framelen;
                                item = item->pNext;
                            }
                            burst_media_add_play_audio(frame, datalen, framecount);
                            wl_free(frame);
                        }
                    }
                } else {
                    WLLOGD("player left frames %d", burst_media_get_player_left_frames());
                }
            }
        }
    }
}

static void burst_voip_state(void) {
    if (WLSDK_VOIP_NONE != s_burst_global.burst_voip.session_voip.state) {
        burst_sdk_message_callback(WLSDK_MSG_VOIP_STATE,
                                   WLSDK_RESULT_SUCCESS,
                                   &s_burst_global.burst_voip.session_voip,
                                   sizeof(wlsdk_session_voip));
    }
}

static void burst_voip_delay_timer_callback(WL_TIMER timer, void* context) {
    process_burst_voip_accept(wl_null, 0);
}

static void burst_voip_incoming(void) {
    burst_sdk_message_callback(WLSDK_MSG_VOIP_INCOMING,
                               WLSDK_RESULT_SUCCESS,
                               &s_burst_global.burst_voip.session_voip,
                               sizeof(wlsdk_session_voip));

    burst_voip_ring();

    // 播放振铃
    burst_media_play_tone(BURST_MEDIA_TONE_VOIP_INCOMING,
                          burst_burst_media_player_end_callback,
                          BURST_MEDIA_PRORITY_VOIP_TONE,
                          BURST_VOIP_LIFE_LEFT,
                          wl_null);

    if (s_burst_global.voip_auto_accept) {
        if (WL_INVALID_TIMER != s_burst_global.voip_delay_timer) {
            wl_timer_stop(s_burst_global.voip_delay_timer);
            s_burst_global.voip_delay_timer = WL_INVALID_TIMER;
        }
        // 延时接听
        s_burst_global.voip_delay_timer = wl_timer_start(s_burst_global.voip_delay_ms,
                                                         NON_REPETITIVE,
                                                         burst_voip_delay_timer_callback,
                                                         wl_null,
                                                         WL_TASK_APP,
                                                         "burst_voip_accept");
        if (WL_INVALID_TIMER == s_burst_global.voip_delay_timer) {
            WLLOGW("wl_timer_start fail");
            return;
        }
    }
}

static void burst_voip_ring(void) {
    s_burst_global.burst_voip.session_voip.state = WLSDK_VOIP_RING;
    s_burst_global.burst_voip.lifeleft           = BURST_VOIP_LIFE_LEFT;
    // 停止振铃或其他播放器
    burst_stop_player();
    // 停止其他录音
    burst_stop_recorder(WLSDK_RESULT_SUCCESS);
    burst_voip_state();
}

static void burst_voip_terminate(wl_int32 resultCode) {
    // 停止VOIP
    burst_media_stop_voip();
    burst_media_play_tone(BURST_MEDIA_TONE_VOIP_HANGUP,
                          burst_burst_media_player_end_callback,
                          BURST_MEDIA_PRORITY_ALARM,
                          0,
                          (void*)wl_null);
    burst_sdk_message_callback(WLSDK_MSG_VOIP_TERMINATE, resultCode, wl_null, 0);
    memset(&s_burst_global.burst_voip, 0, sizeof(s_burst_global.burst_voip));
}

static void burst_voip_establish(void) {
    s_burst_global.burst_voip.session_voip.state = WLSDK_VOIP_ESTABLISH;
    s_burst_global.burst_voip.lifeleft           = BURST_VOIP_LIFE_LEFT;

    // 停止振铃或其他播放器
    burst_stop_player();
    // 停止其他录音
    burst_stop_recorder(WLSDK_RESULT_SUCCESS);
    // 启动voip
    burst_media_start_voip(s_burst_global.burst_voip.session_voip.userid,
                           WLSDK_SESSION_TYPE_SINGLE,
                           burst_burst_meida_voip_recorder_callback,
                           burst_burst_meida_voip_recorder_end_callback,
                           BURST_VOIP_PACKAGE_FRAMES,
                           burst_burst_media_voip_player_callback,
                           burst_burst_media_voip_player_end_callback,
                           wl_null);
    burst_sdk_message_callback(WLSDK_MSG_VOIP_ESTABLISH,
                               WLSDK_RESULT_SUCCESS,
                               &s_burst_global.burst_voip.session_voip,
                               sizeof(wlsdk_session_voip));
    burst_voip_state();
}

static void burst_burst_meida_voip_recorder_callback(wl_uint32 marker,
                                                     wl_uint32 frameslen,
                                                     wl_char*  frames,
                                                     wl_uint32 framecount,
                                                     wl_uint64 sessionid,
                                                     wl_uint32 sessiontype,
                                                     void*     context) {
    burst_record_data* record_data = (burst_record_data*)wl_malloc(sizeof(burst_record_data) + frameslen);
    if (!record_data) {
        WLLOGW("malloc fail");
        return;
    }
    record_data->marker      = marker;
    record_data->framecount  = framecount;
    record_data->sessionid   = sessionid;
    record_data->sessiontype = sessiontype;
    record_data->frameslen   = frameslen;
    record_data->context     = context;
    memcpy(record_data->frames, frames, frameslen);
    wl_core_send_message(
        WL_MODULE_BURST, BURST_MSG_VOIP_RECORD_DATA, record_data, sizeof(burst_record_data) + frameslen);
    wl_free(record_data);
    WLLOGI("wl_core_send_message BURST_MSG_RECORD_DATA framelen %d", frameslen);
}

static void burst_burst_meida_voip_recorder_end_callback(wl_int32 result, wl_bool isclosed, void* context) {
    burst_end_result end_result;
    end_result.resultcode = result;
    end_result.context    = context;
    wl_core_send_message(WL_MODULE_BURST, BURST_MSG_VOIP_RECORD_END, &end_result, sizeof(burst_end_result));
    WLLOGI("wl_core_send_message BURST_MSG_RECORD_END");
}

static void burst_burst_media_voip_player_callback(wl_uint32 framecount, void* context) {
    burst_play_result play_result;
    play_result.framecount = framecount;
    play_result.context    = context;
    wl_core_send_message(WL_MODULE_BURST, BURST_MSG_VOIP_PLAY_RESULT, &play_result, sizeof(burst_play_result));
    WLLOGT("wl_core_send_message BURST_MSG_VOIP_PLAY_RESULT");
}

static void burst_burst_media_voip_player_end_callback(wl_int32 result, wl_bool isclosed, void* context) {
    burst_end_result end_result;
    end_result.resultcode = result;
    end_result.context    = context;
    end_result.isclosed   = isclosed;
    wl_core_send_message(WL_MODULE_BURST, BURST_MSG_VOIP_PLAY_END, &end_result, sizeof(burst_end_result));
    WLLOGI("wl_core_send_message BURST_MSG_VOIP_PLAY_END");
}

static void
    process_burst_voip_msgdata_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    wl_uint32 seq = (wl_uint32)context;
    WLLOGI("burst msg result %d", message->resultCode);
    if (s_burst_global.burst_voip.seq == seq) {
        if (WLSDK_RESULT_SUCCESS != message->resultCode) {
            if (WLSDK_RESULT_LOCAL_REQUEST_TIMEOUT != message->resultCode) {
                burst_voip_terminate(message->resultCode);
            }
        } else {
            s_burst_global.burst_voip.lifeleft = BURST_VOIP_LIFE_LEFT;
        }
    }
}

static wl_int32 burst_protocol_client_voip_request_callback(PBWeilaMsg* message,
                                                            void*       content,
                                                            wl_uint32   contentlen,
                                                            void*       context) {
    WLLOGT("message %d resultCode %d", message->message, message->resultCode);
    switch (message->message) {
        case WL_SESSION_MSGDATA_MSG:
            process_burst_voip_msgdata_message(message, content, contentlen, context);
            break;
        case WL_SESSION_VOIP_INVITE_MSG:
            if (burst_media_is_voip_support()) {
                process_burst_voip_invite_message(message, content, contentlen, context);
            }
            break;
        case WL_SESSION_VOIP_ACCEPT_MSG:
            if (burst_media_is_voip_support()) {
                process_burst_voip_accept_message(message, content, contentlen, context);
            }
            break;
        case WL_SESSION_VOIP_HANGUP_MSG:
            break;
        default:
            break;
    }
    return 0;
}

#ifdef __cplusplus
}
#endif
